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、我们首先看到这里定义了两个样式,分别是FocusVisualOptionMarkFocusVisual

这两个是按 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是整个控件的指示器样式,如下所示

image

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

image

 

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"时,就可以从选中状态,切换为不确定状态。

image

 

让我们再回到模板部分,CheckBox在Button的基础上,增加了左侧复选框部分,这里是通过BorderPath/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>

 

显示层级结构如下:

image

 

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>

 

运行效果

demo

 

这个样式的模板保持了跟原版一致,但是当复选框钩选时,使用了填充效果。

实现思路如下:

当选中时,使用触发器来控制背景颜色的切换。

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

posted @ 2026-09-01 12:05  zhaotianff  阅读(15)  评论(0)    收藏  举报