WPF 點選按鈕時更改按鈕樣式介面效果的 XAML 實現方法
在 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 的免費教程視訊,包含了這些細節