1. 程式人生 > 實用技巧 >WPF 點選按鈕時更改按鈕樣式介面效果的 XAML 實現方法

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 的免費教程視訊,包含了這些細節