WPF控件样式和模板全解析-CheckBox控件
CheckBox的默认样式和模板
依旧是使用Blend来查看CheckBox的默认样式和模板
1 <Style x:Key="FocusVisual"> 2 <Setter Property="Control.Template"> 3 <Setter.Value> 4 <ControlTemplate> 5 <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/> 6 </ControlTemplate> 7 </Setter.Value> 8 </Setter> 9 </Style> 10 <Style x:Key="OptionMarkFocusVisual"> 11 <Setter Property="Control.Template"> 12 <Setter.Value> 13 <ControlTemplate> 14 <Rectangle Margin="14,0,0,0" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/> 15 </ControlTemplate> 16 </Setter.Value> 17 </Setter> 18 </Style> 19 <SolidColorBrush x:Key="OptionMark.Static.Background" Color="#FFFFFFFF"/> 20 <SolidColorBrush x:Key="OptionMark.Static.Border" Color="#FF707070"/> 21 <SolidColorBrush x:Key="OptionMark.Static.Glyph" Color="#FF212121"/> 22 <SolidColorBrush x:Key="OptionMark.MouseOver.Background" Color="#FFF3F9FF"/> 23 <SolidColorBrush x:Key="OptionMark.MouseOver.Border" Color="#FF5593FF"/> 24 <SolidColorBrush x:Key="OptionMark.MouseOver.Glyph" Color="#FF212121"/> 25 <SolidColorBrush x:Key="OptionMark.Pressed.Background" Color="#FFD9ECFF"/> 26 <SolidColorBrush x:Key="OptionMark.Pressed.Border" Color="#FF3C77DD"/> 27 <SolidColorBrush x:Key="OptionMark.Pressed.Glyph" Color="#FF212121"/> 28 <SolidColorBrush x:Key="OptionMark.Disabled.Background" Color="#FFE6E6E6"/> 29 <SolidColorBrush x:Key="OptionMark.Disabled.Border" Color="#FFBCBCBC"/> 30 <SolidColorBrush x:Key="OptionMark.Disabled.Glyph" Color="#FF707070"/> 31 <Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}"> 32 <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/> 33 <Setter Property="Background" Value="{StaticResource OptionMark.Static.Background}"/> 34 <Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border}"/> 35 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/> 36 <Setter Property="BorderThickness" Value="1"/> 37 <Setter Property="Template"> 38 <Setter.Value> 39 <ControlTemplate TargetType="{x:Type CheckBox}"> 40 <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True"> 41 <Grid.ColumnDefinitions> 42 <ColumnDefinition Width="Auto"/> 43 <ColumnDefinition Width="*"/> 44 </Grid.ColumnDefinitions> 45 <Border x:Name="checkBoxBorder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> 46 <Grid x:Name="markGrid"> 47 <Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph}" Margin="1" Opacity="0" Stretch="None"/> 48 <Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph}" Margin="2" Opacity="0"/> 49 </Grid> 50 </Border> 51 <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 52 </Grid> 53 <ControlTemplate.Triggers> 54 <Trigger Property="HasContent" Value="true"> 55 <Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual}"/> 56 <Setter Property="Padding" Value="4,-1,0,0"/> 57 </Trigger> 58 <Trigger Property="IsMouseOver" Value="true"> 59 <Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Background}"/> 60 <Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Border}"/> 61 <Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/> 62 <Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/> 63 </Trigger> 64 <Trigger Property="IsEnabled" Value="false"> 65 <Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Background}"/> 66 <Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Border}"/> 67 <Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/> 68 <Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/> 69 </Trigger> 70 <Trigger Property="IsPressed" Value="true"> 71 <Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Background}"/> 72 <Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Border}"/> 73 <Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/> 74 <Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/> 75 </Trigger> 76 <Trigger Property="IsChecked" Value="true"> 77 <Setter Property="Opacity" TargetName="optionMark" Value="1"/> 78 <Setter Property="Opacity" TargetName="indeterminateMark" Value="0"/> 79 </Trigger> 80 <Trigger Property="IsChecked" Value="{x:Null}"> 81 <Setter Property="Opacity" TargetName="optionMark" Value="0"/> 82 <Setter Property="Opacity" TargetName="indeterminateMark" Value="1"/> 83 </Trigger> 84 </ControlTemplate.Triggers> 85 </ControlTemplate> 86 </Setter.Value> 87 </Setter> 88 </Style>
拆解CheckBox的样式和模板
CheckBox也属于内容控件,所以整体上来说,它和Button的模板是相似的。但是由于CheckBox有复选框,所以会增加一些额外的元素。
1、我们首先看到这里定义了两个样式,分别是FocusVisual和OptionMarkFocusVisual。
这两个是按 Tab 键切换控件焦点时看到的虚线焦点框,也就是键盘导航焦点指示器样式。
1 <Style x:Key="FocusVisual"> 2 <Setter Property="Control.Template"> 3 <Setter.Value> 4 <ControlTemplate> 5 <Rectangle Margin="2" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/> 6 </ControlTemplate> 7 </Setter.Value> 8 </Setter> 9 </Style> 10 <Style x:Key="OptionMarkFocusVisual"> 11 <Setter Property="Control.Template"> 12 <Setter.Value> 13 <ControlTemplate> 14 <Rectangle Margin="14,0,0,0" StrokeDashArray="1 2" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/> 15 </ControlTemplate> 16 </Setter.Value> 17 </Setter> 18 </Style>
FocusVisual是整个控件的指示器样式,如下所示

OptionMarkFocusVisual是前面复选框部分的指示器样式(注意:未指定Content时才会触发这种样式),如下所示

2、然后再看到一些画刷资源的定义
1 <SolidColorBrush x:Key="OptionMark.Static.Background" Color="#FFFFFFFF"/> 2 <SolidColorBrush x:Key="OptionMark.Static.Border" Color="#FF707070"/> 3 <SolidColorBrush x:Key="OptionMark.Static.Glyph" Color="#FF212121"/> 4 <SolidColorBrush x:Key="OptionMark.MouseOver.Background" Color="#FFF3F9FF"/> 5 <SolidColorBrush x:Key="OptionMark.MouseOver.Border" Color="#FF5593FF"/> 6 <SolidColorBrush x:Key="OptionMark.MouseOver.Glyph" Color="#FF212121"/> 7 <SolidColorBrush x:Key="OptionMark.Pressed.Background" Color="#FFD9ECFF"/> 8 <SolidColorBrush x:Key="OptionMark.Pressed.Border" Color="#FF3C77DD"/> 9 <SolidColorBrush x:Key="OptionMark.Pressed.Glyph" Color="#FF212121"/> 10 <SolidColorBrush x:Key="OptionMark.Disabled.Background" Color="#FFE6E6E6"/> 11 <SolidColorBrush x:Key="OptionMark.Disabled.Border" Color="#FFBCBCBC"/> 12 <SolidColorBrush x:Key="OptionMark.Disabled.Glyph" Color="#FF707070"/>
3、接下来就是样式的属性设置部分,这里依旧是不做过多介绍,对CheckBox控件属性不熟悉的小伙伴可以参考以下链接:
https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/controls/checkbox
1 <Style x:Key="CheckBoxStyle1" TargetType="{x:Type CheckBox}"> 2 <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/> 3 <Setter Property="Background" Value="{StaticResource OptionMark.Static.Background}"/> 4 <Setter Property="BorderBrush" Value="{StaticResource OptionMark.Static.Border}"/> 5 <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/> 6 <Setter Property="BorderThickness" Value="1"/> 7 </Style>
4、然后就是我们的重点,CheckBox控件的模板部分
在了解模板组成之前,我们先来看一下CheckBox的几种状态,分为选中/未选中/不确定/禁用四种状态
当指定IsThreeState="True"时,就可以从选中状态,切换为不确定状态。

让我们再回到模板部分,CheckBox在Button的基础上,增加了左侧复选框部分,这里是通过Border和Path/Rectangle组合来实现。
1 <ControlTemplate TargetType="{x:Type CheckBox}"> 2 <!--外部容器--> 3 <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True"> 4 <!--定义两列--> 5 <Grid.ColumnDefinitions> 6 <ColumnDefinition Width="Auto"/> 7 <ColumnDefinition Width="*"/> 8 </Grid.ColumnDefinitions> 9 <!--复选框的外框--> 10 <Border x:Name="checkBoxBorder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> 11 <Grid x:Name="markGrid"> 12 <!--复选框中的钩(确认状态)--> 13 <Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph}" Margin="1" Opacity="0" Stretch="None"/> 14 <!--复选框中的小方框(不确认状态)--> 15 <Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph}" Margin="2" Opacity="0"/> 16 </Grid> 17 </Border> 18 <!--内容显示--> 19 <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 20 </Grid> 21 </ControlTemplate>
显示层级结构如下:

5、最后就是一些触发器
1 <ControlTemplate.Triggers> 2 <!--是否设置了控件内容--> 3 <Trigger Property="HasContent" Value="true"> 4 <Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual}"/> 5 <Setter Property="Padding" Value="4,-1,0,0"/> 6 </Trigger> 7 <!--鼠标是否悬停--> 8 <Trigger Property="IsMouseOver" Value="true"> 9 <Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Background}"/> 10 <Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Border}"/> 11 <Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/> 12 <Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/> 13 </Trigger> 14 <!--是否启用--> 15 <Trigger Property="IsEnabled" Value="false"> 16 <Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Background}"/> 17 <Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Border}"/> 18 <Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/> 19 <Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/> 20 </Trigger> 21 <!--是否按下--> 22 <Trigger Property="IsPressed" Value="true"> 23 <Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Background}"/> 24 <Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Border}"/> 25 <Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/> 26 <Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/> 27 </Trigger> 28 <!--是否选中--> 29 <Trigger Property="IsChecked" Value="true"> 30 <Setter Property="Opacity" TargetName="optionMark" Value="1"/> 31 <Setter Property="Opacity" TargetName="indeterminateMark" Value="0"/> 32 </Trigger> 33 <!--第三状态--> 34 <Trigger Property="IsChecked" Value="{x:Null}"> 35 <Setter Property="Opacity" TargetName="optionMark" Value="0"/> 36 <Setter Property="Opacity" TargetName="indeterminateMark" Value="1"/> 37 </Trigger> 38 </ControlTemplate.Triggers>
VisualState
我们也可以通过VisualState来设置控件各状态下的效果
| VisualState 名称 | VisualStateGroup 名称 | Description |
|---|---|---|
| Normal | CommonStates | 控件处于正常状态。 |
| MouseOver | CommonStates | 鼠标位于控件上。 |
| Pressed | CommonStates | 按下控制键。 |
| Disabled | CommonStates | 控件已禁用。 |
| Focused | FocusStates | 控件具有键盘焦点。 |
| Unfocused | FocusStates | 控件没有键盘焦点。 |
| Checked | CheckStates | 选中控件。 |
| Unchecked | CheckStates | 未选中该控件。 |
| Indeterminate | CheckStates | 控件处于不确定状态。 |
| Valid | ValidationStates | 该控件有效且没有验证错误。 |
| InvalidFocused | ValidationStates | 控件存在验证错误,并且具有键盘焦点。 |
| InvalidUnfocused | ValidationStates | 控件存在验证错误,但没有键盘焦点。 |
把前面的触发器转换为VisuaState后的写法
1 <ControlTemplate TargetType="{x:Type CheckBox}"> 2 3 <!--........省略模板部分.............--> 4 5 <!--VisualStateManager 状态组--> 6 <VisualStateManager.VisualStateGroups> 7 <!--通用状态--> 8 <VisualStateGroup x:Name="CommonStates"> 9 <VisualState x:Name="Normal"/> 10 11 <!--鼠标划过状态--> 12 <VisualState x:Name="MouseOver"> 13 <Storyboard> 14 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="checkBoxBorder" Storyboard.TargetProperty="Background"> 15 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.MouseOver.Background}"/> 16 </ObjectAnimationUsingKeyFrames> 17 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="checkBoxBorder" Storyboard.TargetProperty="BorderBrush"> 18 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.MouseOver.Border}"/> 19 </ObjectAnimationUsingKeyFrames> 20 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="optionMark" Storyboard.TargetProperty="Fill"> 21 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.MouseOver.Glyph}"/> 22 </ObjectAnimationUsingKeyFrames> 23 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="indeterminateMark" Storyboard.TargetProperty="Fill"> 24 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.MouseOver.Glyph}"/> 25 </ObjectAnimationUsingKeyFrames> 26 </Storyboard> 27 </VisualState> 28 29 <!--按下状态--> 30 <VisualState x:Name="Pressed"> 31 <Storyboard> 32 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="checkBoxBorder" Storyboard.TargetProperty="Background"> 33 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.Pressed.Background}"/> 34 </ObjectAnimationUsingKeyFrames> 35 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="checkBoxBorder" Storyboard.TargetProperty="BorderBrush"> 36 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.Pressed.Border}"/> 37 </ObjectAnimationUsingKeyFrames> 38 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="optionMark" Storyboard.TargetProperty="Fill"> 39 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.Pressed.Glyph}"/> 40 </ObjectAnimationUsingKeyFrames> 41 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="indeterminateMark" Storyboard.TargetProperty="Fill"> 42 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.Pressed.Glyph}"/> 43 </ObjectAnimationUsingKeyFrames> 44 </Storyboard> 45 </VisualState> 46 47 <!--禁用状态--> 48 <VisualState x:Name="Disabled"> 49 <Storyboard> 50 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="checkBoxBorder" Storyboard.TargetProperty="Background"> 51 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.Disabled.Background}"/> 52 </ObjectAnimationUsingKeyFrames> 53 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="checkBoxBorder" Storyboard.TargetProperty="BorderBrush"> 54 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.Disabled.Border}"/> 55 </ObjectAnimationUsingKeyFrames> 56 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="optionMark" Storyboard.TargetProperty="Fill"> 57 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.Disabled.Glyph}"/> 58 </ObjectAnimationUsingKeyFrames> 59 <ObjectAnimationUsingKeyFrames Storyboard.TargetName="indeterminateMark" Storyboard.TargetProperty="Fill"> 60 <DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource OptionMark.Disabled.Glyph}"/> 61 </ObjectAnimationUsingKeyFrames> 62 </Storyboard> 63 </VisualState> 64 </VisualStateGroup> 65 66 <!--勾选状态组--> 67 <VisualStateGroup x:Name="CheckStates"> 68 <VisualState x:Name="Unchecked"/> 69 70 <!--钩选状态--> 71 <VisualState x:Name="Checked"> 72 <Storyboard> 73 <DoubleAnimationUsingKeyFrames Storyboard.TargetName="optionMark" Storyboard.TargetProperty="Opacity"> 74 <DiscreteDoubleKeyFrame KeyTime="0" Value="1"/> 75 </DoubleAnimationUsingKeyFrames> 76 <DoubleAnimationUsingKeyFrames Storyboard.TargetName="indeterminateMark" Storyboard.TargetProperty="Opacity"> 77 <DiscreteDoubleKeyFrame KeyTime="0" Value="0"/> 78 </DoubleAnimationUsingKeyFrames> 79 </Storyboard> 80 </VisualState> 81 82 <!--第三状态--> 83 <VisualState x:Name="Indeterminate"> 84 <Storyboard> 85 <DoubleAnimationUsingKeyFrames Storyboard.TargetName="optionMark" Storyboard.TargetProperty="Opacity"> 86 <DiscreteDoubleKeyFrame KeyTime="0" Value="0"/> 87 </DoubleAnimationUsingKeyFrames> 88 <DoubleAnimationUsingKeyFrames Storyboard.TargetName="indeterminateMark" Storyboard.TargetProperty="Opacity"> 89 <DiscreteDoubleKeyFrame KeyTime="0" Value="1"/> 90 </DoubleAnimationUsingKeyFrames> 91 </Storyboard> 92 </VisualState> 93 </VisualStateGroup> 94 </VisualStateManager.VisualStateGroups> 95 </Grid> 96 97 <!-- HasContent 属性触发器,不能放到 VisualState,继续使用触发器实现 --> 98 <ControlTemplate.Triggers> 99 <Trigger Property="HasContent" Value="true"> 100 <Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual}"/> 101 <Setter Property="Padding" Value="4,-1,0,0"/> 102 </Trigger> 103 </ControlTemplate.Triggers> 104 </ControlTemplate>
CheckBox样式示例
1 <Style x:Key="WD.DefaultCheckBox" TargetType="{x:Type CheckBox}"> 2 <Setter Property="Padding" Value="4,0,0,0" /> 3 <Setter Property="Background" Value="{DynamicResource WD.BackgroundBrush}" /> 4 <Setter Property="BorderBrush" Value="{DynamicResource WD.BaseBrush}" /> 5 <Setter Property="BorderThickness" Value="1" /> 6 <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/> 7 <Setter Property="Template"> 8 <Setter.Value> 9 <ControlTemplate TargetType="{x:Type CheckBox}"> 10 <!--继续使用默认模板--> 11 <Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True"> 12 <Grid.ColumnDefinitions> 13 <ColumnDefinition Width="Auto"/> 14 <ColumnDefinition Width="*"/> 15 </Grid.ColumnDefinitions> 16 <Border x:Name="checkBoxBorder" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"> 17 <Grid x:Name="markGrid"> 18 <Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{DynamicResource WD.WindowTextBrush}" Margin="1" Opacity="0" Stretch="None"/> 19 <Rectangle x:Name="indeterminateMark" Fill="{DynamicResource WD.PrimaryBrush}" Margin="2" Opacity="0"/> 20 </Grid> 21 </Border> 22 <ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 23 24 <VisualStateManager.VisualStateGroups> 25 <VisualStateGroup x:Name="CheckStates"> 26 <!--选中状态--> 27 <VisualState x:Name="Checked"> 28 <!--使用动画控制钩的显示--> 29 <Storyboard> 30 <DoubleAnimation Storyboard.TargetName="optionMark" Storyboard.TargetProperty="Opacity" To="1" Duration="00:00:.2"> 31 <DoubleAnimation.EasingFunction> 32 <ExponentialEase EasingMode="EaseOut"></ExponentialEase> 33 </DoubleAnimation.EasingFunction> 34 </DoubleAnimation> 35 </Storyboard> 36 </VisualState> 37 <VisualState x:Name="Unchecked" /> 38 <!--使用动画控制第三状态的显示--> 39 <VisualState x:Name="Indeterminate"> 40 <Storyboard> 41 <DoubleAnimation Storyboard.TargetName="indeterminateMark" Storyboard.TargetProperty="Opacity" To="1" Duration="00:00:.2"> 42 <DoubleAnimation.EasingFunction> 43 <ExponentialEase EasingMode="EaseOut"></ExponentialEase> 44 </DoubleAnimation.EasingFunction> 45 </DoubleAnimation> 46 </Storyboard> 47 </VisualState> 48 </VisualStateGroup> 49 </VisualStateManager.VisualStateGroups> 50 </Grid> 51 52 <ControlTemplate.Triggers> 53 <!--使用触发器控制选中时的背景颜色--> 54 <Trigger Property="IsChecked" Value="True"> 55 <Setter TargetName="checkBoxBorder" Property="Background" Value="{DynamicResource WD.PrimaryBrush}" /> 56 <Setter TargetName="checkBoxBorder" Property="BorderBrush" Value="{DynamicResource WD.PrimaryBrush}" /> 57 </Trigger> 58 59 <!--使用触发器控制鼠标划过时的边框颜色--> 60 <Trigger Property="IsMouseOver" Value="True"> 61 <Setter TargetName="checkBoxBorder" Property="BorderBrush" Value="{DynamicResource WD.PrimaryBrush}" /> 62 </Trigger> 63 64 <!--选中时鼠标划过状态--> 65 <!--选中时鼠标划过的边框效果被覆盖了,所以增加了这个触发器--> 66 <MultiTrigger> 67 <MultiTrigger.Conditions> 68 <Condition Property="IsChecked" Value="True" /> 69 <Condition Property="IsMouseOver" Value="True" /> 70 </MultiTrigger.Conditions> 71 <Setter TargetName="checkBoxBorder" Property="Opacity" Value=".8" /> 72 </MultiTrigger> 73 </ControlTemplate.Triggers> 74 </ControlTemplate> 75 </Setter.Value> 76 </Setter> 77 </Style>
运行效果

这个样式的模板保持了跟原版一致,但是当复选框钩选时,使用了填充效果。
实现思路如下:
当选中时,使用触发器来控制背景颜色的切换。
1 <!--使用触发器控制选中时的背景颜色--> 2 <Trigger Property="IsChecked" Value="True"> 3 <Setter TargetName="checkBoxBorder" Property="Background" Value="{DynamicResource WD.PrimaryBrush}" /> 4 <Setter TargetName="checkBoxBorder" Property="BorderBrush" Value="{DynamicResource WD.PrimaryBrush}" /> 5 </Trigger>
使用VisualState来控制复选框中“钩”的显示
1 <VisualState x:Name="Checked"> 2 <!--使用动画控制钩的显示--> 3 <Storyboard> 4 <DoubleAnimation Storyboard.TargetName="optionMark" Storyboard.TargetProperty="Opacity" To="1" Duration="00:00:.2"> 5 <DoubleAnimation.EasingFunction> 6 <ExponentialEase EasingMode="EaseOut"></ExponentialEase> 7 </DoubleAnimation.EasingFunction> 8 </DoubleAnimation> 9 </Storyboard> 10 </VisualState>
示例代码
https://github.com/zhaotianff/cnblog-demo-code/tree/main/WPFStyleAndTemplateDemo

浙公网安备 33010602011771号