WPF控件样式和模板全解析-ToggleButton控件

ToggleButton的默认样式和模板

依旧是使用Blend来查看ToggleButton的默认样式和模板

 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 <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
11 <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
12 <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
13 <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
14 <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
15 <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
16 <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
17 <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
18 <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
19 <Style x:Key="ToggleButtonStyle1" TargetType="{x:Type ToggleButton}">
20     <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
21     <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
22     <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
23     <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
24     <Setter Property="BorderThickness" Value="1"/>
25     <Setter Property="HorizontalContentAlignment" Value="Center"/>
26     <Setter Property="VerticalContentAlignment" Value="Center"/>
27     <Setter Property="Padding" Value="1"/>
28     <Setter Property="Template">
29         <Setter.Value>
30             <ControlTemplate TargetType="{x:Type ToggleButton}">
31                 <Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
32                     <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
33                 </Border>
34                 <ControlTemplate.Triggers>
35                     <Trigger Property="Button.IsDefaulted" Value="true">
36                         <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
37                     </Trigger>
38                     <Trigger Property="IsMouseOver" Value="true">
39                         <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
40                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
41                     </Trigger>
42                     <Trigger Property="IsPressed" Value="true">
43                         <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
44                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
45                     </Trigger>
46                     <Trigger Property="IsEnabled" Value="false">
47                         <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
48                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
49                         <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
50                     </Trigger>
51                     <Trigger Property="IsChecked" Value="True">
52                         <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
53                         <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
54                     </Trigger>
55                 </ControlTemplate.Triggers>
56             </ControlTemplate>
57         </Setter.Value>
58     </Setter>
59 </Style>

 

拆解ToggleButton的样式和模板

 1、首先是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>

 

2、再就是一些资源画刷的定义

1     <SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
2     <SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
3     <SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
4     <SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
5     <SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
6     <SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
7     <SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
8     <SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
9     <SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>

 

3、样式属性设置

ToggleButton样式属性这一块也不做详细介绍,可以参考以下链接学习ToggleButton的属性

https://learn.microsoft.com/zh-cn/dotnet/desktop/wpf/controls/togglebutton

https://learn.microsoft.com/zh-cn/dotnet/api/system.windows.controls.primitives.togglebutton

这里设置的基本都是一些比较常见的属性

 1 <Style x:Key="ToggleButtonStyle1" TargetType="{x:Type ToggleButton}">
 2     <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
 3     <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
 4     <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
 5     <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
 6     <Setter Property="BorderThickness" Value="1"/>
 7     <Setter Property="HorizontalContentAlignment" Value="Center"/>
 8     <Setter Property="VerticalContentAlignment" Value="Center"/>
 9     <Setter Property="Padding" Value="1"/>
10 </Style>

 

4、然后就是我们的重点,ToggleButton的模板部分

 1 <ControlTemplate TargetType="{x:Type ToggleButton}">
 2     <Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
 3         <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
 4     </Border>
 5     <ControlTemplate.Triggers>
 6         <Trigger Property="Button.IsDefaulted" Value="true">
 7             <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
 8         </Trigger>
 9         <Trigger Property="IsMouseOver" Value="true">
10             <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
11             <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
12         </Trigger>
13         <Trigger Property="IsPressed" Value="true">
14             <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
15             <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
16         </Trigger>
17         <Trigger Property="IsEnabled" Value="false">
18             <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
19             <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
20             <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
21         </Trigger>
22         <Trigger Property="IsChecked" Value="True">
23             <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
24             <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
25         </Trigger>
26     </ControlTemplate.Triggers>
27 </ControlTemplate>

 

可以看到ToggleButton的模板跟Button是一模一样的,就是Border里面套一个ContentPresenter,常规内容控件也都是这种层级结构。

层级结构如下:

image

 

但是ToggleButton和Button不一样就是它增加了复选功能。

这里我们可以把它理解为CheckBox的Button版本。

因为它也具备选中/未选中/不确定三种状态。

 

 下面是选中/未选中状态的效果演示

 

demo

 

因为在官方的样式里,没有“不确定”状态的相关逻辑,所以即使设置了IsThreeState="true",这个状态暂时也无法在UI上显示出来。

 

VisualState

VisualState NameVisualStateGroup NameDescription
Normal CommonStates 控件处于正常状态。
MouseOver CommonStates 鼠标在控件上
Pressed CommonStates 鼠标按下
Disabled CommonStates 禁用
Focused FocusStates 控件具有键盘焦点
Unfocused FocusStates 控件没有键盘焦点
Checked CheckStates 选中控件
Unchecked CheckStates 未选中控件
Indeterminate CheckStates 第三状态(不确定状态)
Valid ValidationStates 控件有效且没有验证错误
InvalidFocused ValidationStates 控件存在验证错误,并且具有键盘焦点。
InvalidUnfocused ValidationStates 控件存在验证错误,但没有键盘焦点。

 

 

使用VisualState的样式示例如下

 1  <Style x:Key="ToggleButtonStyle2" TargetType="{x:Type ToggleButton}">
 2      <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
 3      <Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
 4      <Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
 5      <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
 6      <Setter Property="BorderThickness" Value="1"/>
 7      <Setter Property="HorizontalContentAlignment" Value="Center"/>
 8      <Setter Property="VerticalContentAlignment" Value="Center"/>
 9      <Setter Property="Padding" Value="1"/>
10      <Setter Property="Template">
11          <Setter.Value>
12              <ControlTemplate TargetType="{x:Type ToggleButton}">
13                  <!--使用默认模板结构-->
14                  <Border x:Name="Border" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
15                      <ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
16                      <VisualStateManager.VisualStateGroups>
17                          <VisualStateGroup x:Name="CommonStates">
18                              <VisualState x:Name="Normal" />
19                              <!--鼠标划过-->
20                              <VisualState x:Name="MouseOver">
21                                  <Storyboard>
22                                      <!--设置背景颜色-->
23                                      <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"
24                                      Storyboard.TargetName="Border">
25                                          <EasingColorKeyFrame KeyTime="0"
26                               Value="{StaticResource ControlMouseOverColor}" />
27                                      </ColorAnimationUsingKeyFrames>
28                                      <!--设置文字颜色-->
29                                      <ColorAnimation Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" 
30                                                      Storyboard.TargetName="contentPresenter" 
31                                                      To="{StaticResource ControlLightColor}" Duration="0:0:0.2"></ColorAnimation>
32                                  </Storyboard>
33                              </VisualState>
34                              <VisualState x:Name="Pressed" />
35                              <!--禁用状态-->
36                              <VisualState x:Name="Disabled">
37                                  <Storyboard>
38                                      <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"
39                                      Storyboard.TargetName="Border">
40                                          <EasingColorKeyFrame KeyTime="0"
41                               Value="{StaticResource DisabledControlDarkColor}" />
42                                      </ColorAnimationUsingKeyFrames>
43                                      <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)"
44                                      Storyboard.TargetName="Border">
45                                          <EasingColorKeyFrame KeyTime="0"
46                               Value="{StaticResource DisabledBorderDarkColor}" />
47                                      </ColorAnimationUsingKeyFrames>
48                                  </Storyboard>
49                              </VisualState>
50                          </VisualStateGroup>
51                          <VisualStateGroup x:Name="CheckStates">
52                              <!--选中状态-->
53                              <VisualState x:Name="Checked">
54                                  <Storyboard>
55                                      <ColorAnimationUsingKeyFrames Storyboard.TargetProperty="(Panel.Background).(SolidColorBrush.Color)"
56                                      Storyboard.TargetName="Border">
57                                          <EasingColorKeyFrame KeyTime="0"
58                               Value="{StaticResource ControlPressedColor}" />
59                                      </ColorAnimationUsingKeyFrames>
60                                      <ColorAnimation Storyboard.TargetProperty="(TextElement.Foreground).(SolidColorBrush.Color)" 
61                      Storyboard.TargetName="contentPresenter" 
62                      To="{StaticResource ControlLightColor}" Duration="0:0:0.2"></ColorAnimation>
63                                  </Storyboard>
64                              </VisualState>
65                              <!--未选中状态-->
66                              <VisualState x:Name="Unchecked" />
67                              <!--不确定状态-->
68                              <VisualState x:Name="Indeterminate" />
69                          </VisualStateGroup>
70                      </VisualStateManager.VisualStateGroups>
71                  </Border>
72              </ControlTemplate>
73          </Setter.Value>
74      </Setter>
75  </Style>

 

运行效果

demo

 

ToggleButton样式示例

在日常使用中,我们一般会改变一下ToggleButton的形态,使之变成一个Switch开关。

 1 <Style x:Key="WD.DefaultToggleButton" TargetType="{x:Type ToggleButton}">
 2     <Setter Property="SnapsToDevicePixels" Value="True" />
 3     <Setter Property="UseLayoutRounding" Value="True" />
 4     <Setter Property="FrameworkElement.OverridesDefaultStyle" Value="True" />
 5     <Setter Property="Focusable" Value="False" />
 6     <Setter Property="Padding" Value="4,0" />
 7     <Setter Property="Background" Value="{DynamicResource WD.BaseBrush}" />
 8     <Setter Property="Template">
 9         <Setter.Value>
10             <ControlTemplate TargetType="{x:Type ToggleButton}">
11                 <Grid Background="Transparent">
12                     <Grid.ColumnDefinitions>
13                         <ColumnDefinition Width="auto"/>
14                         <ColumnDefinition/>
15                     </Grid.ColumnDefinitions>
16                     
17                     <!--外部边框-->
18                     <Border x:Name="PART_Border" Width="40" Height="20" Background="{TemplateBinding Background}" BorderBrush="Transparent" BorderThickness="1" CornerRadius="10">
19                         <!--滚动的椭圆-->    
20                         <Ellipse x:Name="PART_Ellipse" Width="16" Height="16" Margin="2,0" HorizontalAlignment="Left" VerticalAlignment="Center" Fill="{DynamicResource WD.WindowTextBrush}">
21                                 <Ellipse.RenderTransform>
22                                     <TranslateTransform />
23                                 </Ellipse.RenderTransform>
24                         </Ellipse>
25                     </Border>
26                     <!--内容显示-->
27                     <ContentPresenter Grid.Column="1" Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}" ContentTemplate="{TemplateBinding ContentTemplate}" Focusable="False" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
28                     
29                     <VisualStateManager.VisualStateGroups>
30                         <VisualStateGroup x:Name="CommonStates">
31                             <!--正常状态-->
32                             <VisualState x:Name="Normal">
33                                 <Storyboard>
34                                     <!--设置边框颜色-->
35                                     <ColorAnimationUsingKeyFrames 
36           Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" 
37           Storyboard.TargetName="PART_Border">
38                                         <EasingColorKeyFrame KeyTime="0" Value="Transparent" />
39                                     </ColorAnimationUsingKeyFrames>
40                                 </Storyboard>
41                             </VisualState>
42                             <!--鼠标划过状态-->
43                             <VisualState x:Name="MouseOver">
44                                 <Storyboard>
45                                     <!--设置边框颜色-->
46                                     <ColorAnimationUsingKeyFrames 
47                                         Storyboard.TargetProperty="(Border.BorderBrush).(SolidColorBrush.Color)" 
48                                         Storyboard.TargetName="PART_Border">
49                                         <EasingColorKeyFrame KeyTime="0" Value="{StaticResource BorderLightColor}" />
50                                     </ColorAnimationUsingKeyFrames>
51                                 </Storyboard>
52                             </VisualState>
53                         </VisualStateGroup>
54                         <VisualStateGroup x:Name="CheckStates">
55                             <!--选中状态-->
56                             <VisualState x:Name="Checked">
57                                 <Storyboard>
58                                     <!--通过RenderTransform.TranslateTransform平移X坐标来实现选中时的动画效果-->
59                                     <!--对Ellipse进行动画-->
60                                     <DoubleAnimation Storyboard.TargetName="PART_Ellipse" Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="20" Duration="00:00:.3">
61                                         <DoubleAnimation.EasingFunction>
62                                             <CubicEase EasingMode="EaseInOut"/>
63                                         </DoubleAnimation.EasingFunction>
64                                     </DoubleAnimation>
65                                 </Storyboard>
66                             </VisualState>
67                             <!--未选中状态-->
68                             <VisualState x:Name="Unchecked">
69                                 <Storyboard>
70                                     <DoubleAnimation Storyboard.TargetName="PART_Ellipse" Storyboard.TargetProperty="(Ellipse.RenderTransform).(TranslateTransform.X)" To="0" Duration="00:00:.3">
71                                         <DoubleAnimation.EasingFunction>
72                                             <CubicEase EasingMode="EaseInOut"/>
73                                         </DoubleAnimation.EasingFunction>
74                                     </DoubleAnimation>
75                                 </Storyboard>
76                             </VisualState>
77                             <VisualState x:Name="Indeterminate" />
78                         </VisualStateGroup>
79                     </VisualStateManager.VisualStateGroups>
80                 </Grid>
81                 <ControlTemplate.Triggers>
82                     <!--选中状态-->
83                     <!--防止动画导致显示效果不好,使用触发器来实现-->
84                     <Trigger Property="IsChecked" Value="True">
85                         <Setter Property="Background" Value="{DynamicResource WD.PrimaryBrush}" />
86                     </Trigger>
87                 </ControlTemplate.Triggers>
88             </ControlTemplate>
89         </Setter.Value>
90     </Setter>
91 </Style>

 

显示效果如下:

demo

 

说明:“不确定”状态在日常使用中并不常见,所以这里就没有为它设置样式效果了,有需要的小伙伴可以自行补全<VisualState x:Name="Indeterminate" />部分的代码。

 

示例代码

https://github.com/zhaotianff/cnblog-demo-code/tree/main/WPFStyleAndTemplateDemo

posted @ 2026-09-07 10:59  zhaotianff  阅读(17)  评论(0)    收藏  举报