在 WPF 中按鈕 Button 將會吃掉路由事件,此時的 EventTrigger 如果通過 RoutedEvent 是 MouseLeftButtonDown 那么將會拿不到路由事件,也就觸發不了,因此樣式將不會變更。簡單的解決方法就是通過 VisualStateManager 配合 VisualState 來實現
實現效果如下,所有代碼都是 XAML 代碼
實現方式為給 Button 定義一個樣式,通過如下代碼可以定義
<Style TargetType="Button">
</Style>
上面代碼沒有定義樣式資源的 key 因此會對容器內所有的 Button 按鈕樣式生效,因此我將這個樣式放在需要使用的容器里面,這樣才不會干擾其他容器內的元素
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button"></Style>
</StackPanel.Resources>
</StackPanel>
接着新建一個按鈕,如下代碼
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button"></Style>
</StackPanel.Resources>
<Button Margin="10,10,10,10" Width="100" Height="100" Content="Button 1" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</StackPanel>
接下來就是核心邏輯了,通過重寫 Button 的 Template 內容,給內容的 Border 添加一些必要樣式
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border">
<Border.RenderTransform>
<ScaleTransform />
</Border.RenderTransform>
<Grid>
<Rectangle Fill="Blue"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
接着在 Border 添加 VisualStateManager 如下面代碼
<Border x:Name="Border">
<Border.RenderTransform>
<ScaleTransform />
</Border.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Pressed"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Rectangle Fill="Blue"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
可以看到上面代碼有兩個 VisualState 分別是 Normal 和 Pressed 兩個,其中 Pressed 表示的是鼠標按下,因此可以通過在 Pressed 添加動畫實現更改樣式
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
To="0.5" />
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
To="0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
如上面代碼是更改縮放
那么抬起呢?其實抬起就是非 Pressed 也就是 Normal 狀態,啥都不寫將會自動還原為屬性的值。原理是在依賴屬性里面,其實屬性是一個屬性列表,將會取優先級最高的一個,而優先級是這樣排序的
屬性系統強制
活動動畫或具有 Hold 行為的動畫
本地值
TemplatedParent 模板屬性
隱式樣式
樣式觸發器
模板觸發器
樣式資源庫
默認(主題)樣式
繼承
來自依賴屬性元數據的默認值
詳細請看 依賴項屬性值優先級
所有代碼如下
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border">
<Border.RenderTransform>
<ScaleTransform />
</Border.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
To="0.5" />
<DoubleAnimation
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
To="0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Rectangle Fill="Blue"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<Button Margin="10,10,10,10" Width="100" Height="100" Content="Button 1" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</StackPanel>
代碼放在 github 歡迎小伙伴訪問
當然,本文有很多知識點沒有聊到,包括 Style 是什么,以及屬性的配置應該如何寫,還有動畫 DoubleAnimation 是什么等等。我特別推薦小伙伴入門的時候看 微軟技術教程 - 嗶哩嗶哩 ( ゜- ゜)つロ 乾杯~ Bilibili 的免費教程視頻,包含了這些細節