UI 设计中经常会用到 Toggle Button,用于切换不同的状态。
UWP 中有 ToggleSwitch, 长这样:

WPF 中有 ToggleButton, 长这样:

嗯?????(黑人问号脸)

=========== 分割线 ===========

WPF 中需要给 ToggleButton 自定义控件模板,以实现 ToggleSwitch 的效果,先上效果图:

Style:

<ControlTemplate.Resources><Storyboard x:Key="OnChecked"><ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)"Storyboard.TargetName="path"><EasingThicknessKeyFrame KeyTime="0" Value="40,0,0,0"/></ThicknessAnimationUsingKeyFrames></Storyboard><Storyboard x:Key="OnUnchecked"><ThicknessAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Margin)"Storyboard.TargetName="path"><EasingThicknessKeyFrame KeyTime="0" Value="0"/></ThicknessAnimationUsingKeyFrames></Storyboard>
</ControlTemplate.Resources>
<Border x:Name="toggleBorder"CornerRadius="13"Background="Green"Width="60"Height="26"BorderThickness="1"BorderBrush="Black"><Grid><Path x:Name="path"><Path.Fill><LinearGradientBrush StartPoint="1,0" EndPoint="1,1"><GradientStop Color="White"/></LinearGradientBrush></Path.Fill><Path.Data><GeometryGroup><GeometryGroup.Children><EllipseGeometry Center="6,12" RadiusX="9" RadiusY="9"/></GeometryGroup.Children></GeometryGroup></Path.Data></Path></Grid>
</Border>

加上 Trigger:

<ControlTemplate.Triggers><EventTrigger RoutedEvent="ToggleButton.Checked"><BeginStoryboard Storyboard="{StaticResource OnChecked}"/></EventTrigger><EventTrigger RoutedEvent="ToggleButton.Unchecked"><BeginStoryboard Storyboard="{StaticResource OnUnchecked}"/></EventTrigger><Trigger Property="IsChecked" Value="True"><Setter Property="Background" Value="DimGray"/><Setter TargetName="toggleBorder" Property="Background" Value="Green"/><Setter TargetName="toggleBorder" Property="BorderBrush" Value="Green"/></Trigger><Trigger Property="IsChecked" Value="False"><Setter TargetName="toggleBorder" Property="Background" Value="LightGray"/><Setter TargetName="path" Property="Fill"><Setter.Value><LinearGradientBrush StartPoint="1,0" EndPoint="1,1"><GradientStop Color="Gray"/></LinearGradientBrush></Setter.Value></Setter><Setter TargetName="path" Property="Data"><Setter.Value><GeometryGroup><GeometryGroup.Children><EllipseGeometry Center="13,12" RadiusX="9" RadiusY="9"/></GeometryGroup.Children></GeometryGroup></Setter.Value></Setter></Trigger>
</ControlTemplate.Triggers>

到这儿自定义 ToggleButton 就基本完成了,下面是扩展部分。

=============================

扩展一:High Contrast

实际开发中,往往需要控件支持高对比度(High Contrast):

using System.ComponentModel;
using System.Windows;
using System.Windows.Media;namespace ToggleButtonStyle
{public class HighContrastHelper : DependencyObject{private HighContrastHelper(){SystemParameters.StaticPropertyChanged += SystemParameters_StaticPropertyChanged;}~HighContrastHelper(){SystemParameters.StaticPropertyChanged -= SystemParameters_StaticPropertyChanged;}private static HighContrastHelper instance;public static HighContrastHelper Instance{get{if (instance == null)instance = new HighContrastHelper();return instance;}}public void ApplyCurrentTheme(){if (SystemParameters.HighContrast){SolidColorBrush windowbrush = SystemColors.WindowBrush;if (windowbrush.Color.R == 255 && windowbrush.Color.G == 255 && windowbrush.Color.B == 255){Instance.IsHighContrastWhite = true;Instance.IsHighContrastBlack = false;}else if (windowbrush.Color.R == 0 && windowbrush.Color.G == 0 && windowbrush.Color.B == 0){Instance.IsHighContrastWhite = false;Instance.IsHighContrastBlack = true;}else{Instance.IsHighContrastWhite = false;Instance.IsHighContrastBlack = false;}}}void SystemParameters_StaticPropertyChanged(object sender, PropertyChangedEventArgs e){if (e.PropertyName == "HighContrast"){ApplyCurrentTheme();}}public static readonly DependencyProperty IsHighContrastWhiteProperty = DependencyProperty.Register("IsHighContrastWhite",typeof(bool),typeof(HighContrastHelper),new PropertyMetadata(false));public static readonly DependencyProperty IsHighContrastBlackProperty = DependencyProperty.Register("IsHighContrastBlack",typeof(bool),typeof(HighContrastHelper),new PropertyMetadata(false));public bool IsHighContrastWhite{get { return (bool)GetValue(IsHighContrastWhiteProperty); }private set { SetValue(IsHighContrastWhiteProperty, value); }}public bool IsHighContrastBlack{get { return (bool)GetValue(IsHighContrastBlackProperty); }private set { SetValue(IsHighContrastBlackProperty, value); }}}
}

注:系统事件在析构函数中最好取消订阅,以减小内存开销。

然后增加相应的 Trigger:

<MultiDataTrigger><MultiDataTrigger.Conditions><Condition Binding="{Binding Path=IsHighContrastWhite, Source={x:Static local:HighContrastHelper.Instance}}" Value="true" /><Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" Value="true"/><Condition Binding="{Binding ElementName=toggleButton, Path=IsChecked}" Value="true"/></MultiDataTrigger.Conditions><Setter TargetName="toggleBorder" Property="Background" Value="Blue"/>
</MultiDataTrigger>
<MultiDataTrigger><MultiDataTrigger.Conditions><Condition Binding="{Binding Path=IsHighContrastWhite, Source={x:Static local:HighContrastHelper.Instance}}" Value="true" /><Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" Value="true"/><Condition Binding="{Binding ElementName=toggleButton, Path=IsChecked}" Value="false"/></MultiDataTrigger.Conditions><Setter TargetName="toggleBorder" Property="Background" Value="Black"/>
</MultiDataTrigger>
<MultiDataTrigger><MultiDataTrigger.Conditions><Condition Binding="{Binding Path=IsHighContrastBlack, Source={x:Static local:HighContrastHelper.Instance}}" Value="true" /><Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" Value="true"/><Condition Binding="{Binding ElementName=toggleButton, Path=IsChecked}" Value="true"/></MultiDataTrigger.Conditions><Setter TargetName="toggleBorder" Property="Background" Value="Aqua"/>
</MultiDataTrigger>
<MultiDataTrigger><MultiDataTrigger.Conditions><Condition Binding="{Binding Path=IsHighContrastBlack, Source={x:Static local:HighContrastHelper.Instance}}" Value="true" /><Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag}" Value="true"/><Condition Binding="{Binding ElementName=toggleButton, Path=IsChecked}" Value="false"/></MultiDataTrigger.Conditions><Setter TargetName="toggleBorder" Property="Background" Value="Yellow"/>
</MultiDataTrigger>

要控件支持高对比度,还需给控件设置 Tag 属性:

<Setter Property="Tag" Value="{DynamicResource {x:Static SystemParameters.HighContrastKey}}"/>

效果图:

High Contrast White On:

High Contrast White Off:
High Contrast Black On:

High Contrast Black Off:

扩展二:Accessibility

开发中往往还需要支持 Narrator 读取文本:

TabIndex="1"
AutomationProperties.Name="CustomizedToggle"

自动化测试 AutomationID:

AutomationProperties.AutomationId="CustomizedToggle"

需要注意的一点是当打开 Narrator,使用键盘的 Enter/Space 键切换 ToggleButton 的时候,其 Click 事件往往不能触发,这时候需要使用其 Checked/UnChecked 事件。

最后附上源码:WPF Toggle Button Style

That’s All, thx~

PS:原创不易,转载请注明出处。

WPF 自定义 ToggleButton 样式相关推荐

  1. WPF 自定义ToggleButton样式

    废话不多说! 样式显示如图 源代码奉上: <LinearGradientBrush x:Key="ButtonNormalBackgroundFill" EndPoint=& ...

  2. WPF 自定义CheckBox样式

    引: WPF 自定义CheckBox样式 - 一叶知秋,知寒冬 - 博客园 Checkbox基本样式 下面的样式包含了CheckBox三种状态的显示,这里CheckBox的三种状态是使用图片代替的.当 ...

  3. WPF自定义Slider样式外观 实现酷狗播放进度条

    自定义滑块左边和右边的样式以及滑块的样式 关键代码有注释说明 效果: VS2015下测试: VSBlend生成的模板 <Style x:Key="SliderStyle2" ...

  4. WPF 自定义Expander样式

    先看效果 样式代码如下: <Style x:Key="ExpanderStyle" TargetType="{x:Type Expander}">& ...

  5. WPF自定义圆形按钮样式

    转帖链接:https://www.itsvse.com/thread-3348-1-1.html 新建Style.xaml,将一下代码填入,xmlns:local改为自己的 <ResourceD ...

  6. WPF自定义控件与样式(8)-ComboBox与自定义多选控件MultComboBox

    一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: 下拉选 ...

  7. WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展

    原文:WPF自定义控件与样式(5)-Calendar/DatePicker日期控件自定义样式及扩展 一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐 ...

  8. WPF自定义控件与样式(4)-CheckBox/RadioButton自定义样式

    原文:WPF自定义控件与样式(4)-CheckBox/RadioButton自定义样式 一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等, ...

  9. 【C#】wpf自定义calendar日期选择控件的样式

    原文:[C#]wpf自定义calendar日期选择控件的样式 首先上图看下样式 原理 总览 ItemsControl内容的生成 实现 界面的实现 后台ViewModel的实现 首先上图,看下样式 原理 ...

最新文章

  1. 信息记录拉取失败_天猫入驻为什么失败?猫店侠做详细解读
  2. android100 自定义内容提供者
  3. 【android-tips】installfailedinsufficientstorage解决方案
  4. STM32开发 -- STM32初识
  5. 【多线程】:Synchronized和ReentrantLock的对比
  6. 数据库函数..........
  7. 勘误:EOS资源抵押退还
  8. Python使用yagmail库实现发送邮件功能
  9. kno DNS 03 Tips - DNS Cookies
  10. 一张图了解CAS单点登录的流程
  11. Python 内置函数详解
  12. Horizontalscrollview
  13. 【Linux的开胃小菜】基于Ubuntu搭建内网DNS服务器
  14. 基于vue的个人博客
  15. c语言贺卡代码大全,C++实现新年贺卡程序
  16. CSS生日快乐:CSS之父Håkon Wium Lie访谈录
  17. 由于找不到C:\InetPub\ftproot\Tipray\Ldterm\ghijt32.DLL,无法继续执行代码。重新安装程序可能会解决此问题。
  18. 白噪声,有色噪声的定义、特性及其MATLAB仿真
  19. oracle rac 心跳参数 misscount disktimeout
  20. PaddlePaddle飞桨论文复现营——3D Residual Networks for Action Recognition学习笔记

热门文章

  1. 《实用机器学习》(孙亮 黄倩.著)笔记——第二章 R语言
  2. 《脑的争论:先天还是后天?》约翰·E.道林阐述脑发育成熟老化研究进展(11400字)(附1书1文PDF公号发“脑的争论”下载)
  3. JVM -- 垃圾回收;垃圾回收算法(三)
  4. 牛客练习赛56 小魂和他的数列
  5. 前端与服务器通讯的数据交换格式XML 、JSON
  6. 浏览器g.xxx333xxx.com 跳转2345问题解决方法
  7. 深度linux安装安卓软件,Ubuntu 用户安装深度软件中心教程
  8. 论文降重脚本思路(根据词性)
  9. mysql 数据库的下载与安装 ,以及一些简单命令(任务查找,结束任务等)
  10. 联想笔记本长时间不用后无法充电问题及解决