WPF 中如何将 CheckBox 转换为开关样式

400 阅读3分钟

前言

本文将详细介绍如何在WPF应用程序中实现这一转换,通过自定义样式和模板,使CheckBox控件呈现出开关的外观。无论是 WPF 的手还是经验丰富的大佬,本文提供清晰的步骤和实用的技巧,帮助大家轻松创建美观且功能强大的开关控件。

效果

先看一下效果

isChecked = false 的时候的效果

isChecked = true 的时候的效果

然后我们来实现一下这个效果吧

正文

第一步:创建一个空的 WPF 项目;

第二步:在项目里面添加一个checkbox

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

这个时候的checkbox的样子是这样的

第三步:在页面中右键checkbox,选择 编辑模板 ,再 编辑副本, 之后确定

VS 就会给我们自动生成一个名为"CheckBoxStyle1"的Checkbox的默认样式的代码,我们通过修改默认样式的代码,把普通的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>

总结

通过本文学习了如何在WPF中将标准的CheckBox控件转换为现代化的开关样式。我们使用XAML样式和控件模板来自定义CheckBox的外观,并通过几个简单的步骤实现了这一目标。

这种转换不仅提升了用户界面的美观度,还增强了用户体验。希望这些内容能帮助大家在WPF项目中创造出更加吸引人的用户界面。

最后

如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。

也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

优秀是一种习惯,欢迎大家留言学习!

作者:BearHan

出处:cnblogs.com/lvpp13/p/18363225

声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!