WPF 怎麼把checkbox改成開關樣式

BearHan發表於2024-08-16

先看一下效果吧:

isChecked = false 的時候的效果

isChecked = true 的時候的效果

然後我們來實現一下這個效果吧

第一步:建立一個空的wpf專案;

第二步:在專案裡面新增一個checkbox

    <Grid>
        <CheckBox HorizontalAlignment="Center" IsChecked="True"
                  BorderBrush="Black" VerticalAlignment="Center" 
                  Content="switch" Background="#FF00ADFF"/>
    </Grid>

這個時候的checkbox的樣子是這樣的

第三步:在頁面中右鍵checkbox,選擇 編輯模板 ,再 編輯副本, 之後確定

vs就會給我們自動生成一個名為 CheckBoxStyle1Checkbox的預設樣式的程式碼,我們透過修改預設樣式的程式碼,把普通的Checkbox變成一個開關。

第四步:修改預設樣式

                        <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Border x:Name="PART_Border" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" 
                                    BorderThickness="1" Height="14" Width="25" CornerRadius="5">
                                <Border x:Name="SwitchBorder" BorderThickness="1" BorderBrush="Gray" Background="White" Width="10" 
                                        Margin="1" CornerRadius="5" HorizontalAlignment="Right"/>
                            </Border>
                            <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                              Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Grid>

把之前的樣式程式碼改成上面的程式碼,trigger部分,把報錯的部分全部刪除

這個時候,我們的開關樣式就已經完成了

我們現在需要新增一些trigger和動畫來實現切換效果

第五步:新增動畫和trigger

<Storyboard x:Key="SwitchChecked">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchBorder" Storyboard.TargetProperty="(FrameworkElement.Width)">
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="10"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.2500000" Value="20"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="10"/>
    </DoubleAnimationUsingKeyFrames>
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchBorder" Storyboard.TargetProperty="(FrameworkElement.HorizontalAlignment)">
        <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static HorizontalAlignment.Left}"/>
        <DiscreteObjectKeyFrame KeyTime="00:00:00.2500000" Value="{x:Static HorizontalAlignment.Right}"/>
        <DiscreteObjectKeyFrame KeyTime="00:00:00.5000000" Value="{x:Static HorizontalAlignment.Right}"/>
    </ObjectAnimationUsingKeyFrames>
</Storyboard>

<Storyboard x:Key="SwitchUnchecked">
    <DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchBorder" Storyboard.TargetProperty="(FrameworkElement.Width)">
        <EasingDoubleKeyFrame KeyTime="00:00:00" Value="10"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.2500000" Value="20"/>
        <EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="10"/>
    </DoubleAnimationUsingKeyFrames>
    <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchBorder" Storyboard.TargetProperty="(FrameworkElement.HorizontalAlignment)">
        <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static HorizontalAlignment.Right}"/>
        <DiscreteObjectKeyFrame KeyTime="00:00:00.2500000" Value="{x:Static HorizontalAlignment.Left}"/>
        <DiscreteObjectKeyFrame KeyTime="00:00:00.5000000" Value="{x:Static HorizontalAlignment.Left}"/>
    </ObjectAnimationUsingKeyFrames>
</Storyboard>

上面這段程式碼就是表示不同狀態下的不同動畫效果,透過改變SwitchBoder的寬度和對齊方式,就可以實現了

然後我們再把這段動畫效果運用到模板中,再新增3個trigger,就可以了

                        <ControlTemplate.Triggers>
                            <Trigger Property="HasContent" Value="true">
                                <Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual}"/>
                                <Setter Property="Padding" Value="4,-1,0,0"/>
                            </Trigger>
                            <EventTrigger RoutedEvent="{x:Static CheckBox.CheckedEvent}">
                                <BeginStoryboard Storyboard="{StaticResource SwitchChecked}"/>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="{x:Static CheckBox.UncheckedEvent}">
                                <BeginStoryboard Storyboard="{StaticResource SwitchUnchecked}"/>
                            </EventTrigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="PART_Border" Value="{StaticResource OptionMark.Disabled.Background}"/>
                                <Setter Property="BorderBrush" TargetName="PART_Border" Value="{StaticResource OptionMark.Disabled.Border}"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="False">
                                <Setter Property="Background" Value="White" TargetName="PART_Border"/>
                                <Setter Property="HorizontalAlignment" Value="Left" TargetName="SwitchBorder"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="BorderBrush" TargetName="PART_Border" Value="{StaticResource OptionMark.MouseOver.Border}"/>
                            </Trigger>

                        </ControlTemplate.Triggers>

到現在,樣式和動畫就已經完成了,我們再把程式碼全部剪下到App.xaml這個專案資原始檔下面,再刪掉style的命名,x:Key="CheckBoxStyle1"刪掉,

這樣子我們的專案裡面的checkbox就都是開關的樣式了,執行專案也不會報錯啦,最後的程式碼如下

    <Application.Resources>

        <Storyboard x:Key="SwitchChecked">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchBorder" Storyboard.TargetProperty="(FrameworkElement.Width)">
                <EasingDoubleKeyFrame KeyTime="00:00:00" Value="10"/>
                <EasingDoubleKeyFrame KeyTime="00:00:00.2500000" Value="20"/>
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="10"/>
            </DoubleAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchBorder" Storyboard.TargetProperty="(FrameworkElement.HorizontalAlignment)">
                <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static HorizontalAlignment.Left}"/>
                <DiscreteObjectKeyFrame KeyTime="00:00:00.2500000" Value="{x:Static HorizontalAlignment.Right}"/>
                <DiscreteObjectKeyFrame KeyTime="00:00:00.5000000" Value="{x:Static HorizontalAlignment.Right}"/>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>

        <Storyboard x:Key="SwitchUnchecked">
            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="SwitchBorder" Storyboard.TargetProperty="(FrameworkElement.Width)">
                <EasingDoubleKeyFrame KeyTime="00:00:00" Value="10"/>
                <EasingDoubleKeyFrame KeyTime="00:00:00.2500000" Value="20"/>
                <EasingDoubleKeyFrame KeyTime="00:00:00.5000000" Value="10"/>
            </DoubleAnimationUsingKeyFrames>
            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="SwitchBorder" Storyboard.TargetProperty="(FrameworkElement.HorizontalAlignment)">
                <DiscreteObjectKeyFrame KeyTime="00:00:00" Value="{x:Static HorizontalAlignment.Right}"/>
                <DiscreteObjectKeyFrame KeyTime="00:00:00.2500000" Value="{x:Static HorizontalAlignment.Left}"/>
                <DiscreteObjectKeyFrame KeyTime="00:00:00.5000000" Value="{x:Static HorizontalAlignment.Left}"/>
            </ObjectAnimationUsingKeyFrames>
        </Storyboard>


        <Style x:Key="FocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <Style x:Key="OptionMarkFocusVisual">
            <Setter Property="Control.Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Rectangle Margin="14,0,0,0" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        <SolidColorBrush x:Key="OptionMark.Static.Background" Color="#FFFFFFFF"/>
        <SolidColorBrush x:Key="OptionMark.Static.Border" Color="#FF707070"/>
        <SolidColorBrush x:Key="OptionMark.Static.Glyph" Color="#FF212121"/>
        <SolidColorBrush x:Key="OptionMark.MouseOver.Background" Color="#FFF3F9FF"/>
        <SolidColorBrush x:Key="OptionMark.MouseOver.Border" Color="#FF5593FF"/>
        <SolidColorBrush x:Key="OptionMark.MouseOver.Glyph" Color="#FF212121"/>
        <SolidColorBrush x:Key="OptionMark.Pressed.Background" Color="#FFD9ECFF"/>
        <SolidColorBrush x:Key="OptionMark.Pressed.Border" Color="#FF3C77DD"/>
        <SolidColorBrush x:Key="OptionMark.Pressed.Glyph" Color="#FF212121"/>
        <SolidColorBrush x:Key="OptionMark.Disabled.Background" Color="#FFE6E6E6"/>
        <SolidColorBrush x:Key="OptionMark.Disabled.Border" Color="#FFBCBCBC"/>
        <SolidColorBrush x:Key="OptionMark.Disabled.Glyph" Color="#FF707070"/>
        <Style TargetType="{x:Type CheckBox}">
            <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
            <Setter Property="Background" Value="{StaticResource OptionMark.Static.Background}"/>
            <Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border}"/>
            <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
            <Setter Property="BorderThickness" Value="1"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type CheckBox}">
                        <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="Auto"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <Border x:Name="PART_Border" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" 
                                    BorderThickness="1" Height="14" Width="25" CornerRadius="5">
                                <Border x:Name="SwitchBorder" BorderThickness="1" BorderBrush="Gray" Background="White" Width="10" 
                                        Margin="1" CornerRadius="5" HorizontalAlignment="Right"/>
                            </Border>
                            <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
                                              Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                        </Grid>
                        <ControlTemplate.Triggers>
                            <Trigger Property="HasContent" Value="true">
                                <Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual}"/>
                                <Setter Property="Padding" Value="4,-1,0,0"/>
                            </Trigger>
                            <EventTrigger RoutedEvent="{x:Static CheckBox.CheckedEvent}">
                                <BeginStoryboard Storyboard="{StaticResource SwitchChecked}"/>
                            </EventTrigger>
                            <EventTrigger RoutedEvent="{x:Static CheckBox.UncheckedEvent}">
                                <BeginStoryboard Storyboard="{StaticResource SwitchUnchecked}"/>
                            </EventTrigger>
                            <Trigger Property="IsEnabled" Value="false">
                                <Setter Property="Background" TargetName="PART_Border" Value="{StaticResource OptionMark.Disabled.Background}"/>
                                <Setter Property="BorderBrush" TargetName="PART_Border" Value="{StaticResource OptionMark.Disabled.Border}"/>
                            </Trigger>
                            <Trigger Property="IsChecked" Value="False">
                                <Setter Property="Background" Value="White" TargetName="PART_Border"/>
                                <Setter Property="HorizontalAlignment" Value="Left" TargetName="SwitchBorder"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="BorderBrush" TargetName="PART_Border" Value="{StaticResource OptionMark.MouseOver.Border}"/>
                            </Trigger>

                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
        
    </Application.Resources>

下面是廣告:

專案github地址:bearhanQ/WPFFramework: Share some experience (github.com)

QQ技術交流群:332035933;

相關文章