惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
月光博客
月光博客
T
Tailwind CSS Blog
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Last Week in AI
Last Week in AI
大猫的无限游戏
大猫的无限游戏
S
SegmentFault 最新的问题
罗磊的独立博客
Jina AI
Jina AI
酷 壳 – CoolShell
酷 壳 – CoolShell
宝玉的分享
宝玉的分享
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 三生石上(FineUI控件)
量子位
雷峰网
雷峰网
Apple Machine Learning Research
Apple Machine Learning Research
美团技术团队
博客园 - 聂微东
V
V2EX

博客园 - zhaotianff

WPF控件样式和模板全解析-RadioButton控件 WPF控件样式和模板全解析-CheckBox控件 软件架构风格与软件架构设计方法 WPF控件样式和模板全解析-Label控件 WPF控件样式和模板全解析-Button控件 Windows一直卡在检查更新的解决方案 - zhaotianff - 博客园 AI时代,程序员应该何去何从? C++中的explicit关键字 如何解决Windows 10/11 删除打印机时,拒绝访问的问题 解决IIS服务器typecho上传附件后无法访问的问题 Visual Studio编译过程中拷贝文件的一些实用小技巧 Jenkins快速入门教程 .NET是什么?为什么要选择.NET? 如何在WPF中实现类似DevOps的自动化流程 一文带你搞懂医疗器械设计开发全生命周期 Windows如何清除本机记录的git用户名和密码 HL7协议详解 医疗行业 GDT 数据格式详解 一文带你搞懂C# 异步编程(async/await)底层原理 如何在C#中使用Chromium headless(无头模式)浏览器 不同.NET版本中的WPF新增功能 如何在WPF中使用 Fluent 主题 Windows平台下的各种原生UI框架介绍 C#如何Hook托管函数 WinDbg 用户层调试进阶教程 Windows编程的一些基础理论 推荐一款优秀的Windows经典文件管理器项目-WinFile Windows如何阻止应用程序联网 如何在WPF中捕获窗口外的事件 如何查看Windows进程的启动来源
WPF控件样式和模板全解析-ToggleButton控件
zhaotianff · 2026-09-07 · via 博客园 - zhaotianff

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的样式和模板

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

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

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"/>

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>

然后就是我们的重点,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

示例代码

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