WPF 点击按钮时更改按钮样式界面效果的 XAML 实现方法
在 WPF 中按钮 Button 将会吃掉路由事件,此时的 EventTrigger 如果通过 RoutedEvent 是 MouseLeftButtonDown 那么将会拿不到路由事件,也就触发不了,因此样式将不会变更。简单的解决方法就是通过 VisualStateManager 配合 VisualState 来实现
实现效果如下,所有代码都是 XAML 代码
实现方式为给 Button 定义一个样式,通过如下代码可以定义
<Style TargetType="Button">
</Style>
上面代码没有定义样式资源的 key 因此会对容器内所有的 Button 按钮样式生效,因此我将这个样式放在需要使用的容器里面,这样才不会干扰其他容器内的元素
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button"></Style>
</StackPanel.Resources>
</StackPanel>
接着新建一个按钮,如下代码
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button"></Style>
</StackPanel.Resources>
<Button Margin="10,10,10,10" Width="100" Height="100" Content="Button 1" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</StackPanel>
接下来就是核心逻辑了,通过重写 Button 的 Template 内容,给内容的 Border 添加一些必要样式
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border">
<Border.RenderTransform>
<ScaleTransform />
</Border.RenderTransform>
<Grid>
<Rectangle Fill="Blue"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
接着在 Border 添加 VisualStateManager 如下面代码
<Border x:Name="Border">
<Border.RenderTransform>
<ScaleTransform />
</Border.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Pressed"/>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Rectangle Fill="Blue"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
可以看到上面代码有两个 VisualState 分别是 Normal 和 Pressed 两个,其中 Pressed 表示的是鼠标按下,因此可以通过在 Pressed 添加动画实现更改样式
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
To="0.5" />
<DoubleAnimation Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
To="0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
如上面代码是更改缩放
那么抬起呢?其实抬起就是非 Pressed 也就是 Normal 状态,啥都不写将会自动还原为属性的值。原理是在依赖属性里面,其实属性是一个属性列表,将会取优先级最高的一个,而优先级是这样排序的
属性系统强制
活动动画或具有 Hold 行为的动画
本地值
TemplatedParent 模板属性
隐式样式
样式触发器
模板触发器
样式资源库
默认(主题)样式
继承
来自依赖属性元数据的默认值
详细请看 依赖项属性值优先级
所有代码如下
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border">
<Border.RenderTransform>
<ScaleTransform />
</Border.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimation
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)"
To="0.5" />
<DoubleAnimation
Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)"
To="0.5" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid>
<Rectangle Fill="Blue"/>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<Button Margin="10,10,10,10" Width="100" Height="100" Content="Button 1" HorizontalAlignment="Center"
VerticalAlignment="Center" />
</StackPanel>
代码放在 github 欢迎小伙伴访问
当然,本文有很多知识点没有聊到,包括 Style 是什么,以及属性的配置应该如何写,还有动画 DoubleAnimation 是什么等等。我特别推荐小伙伴入门的时候看 微软技术教程 - 哔哩哔哩 ( ゜- ゜)つロ 乾杯~ Bilibili 的免费教程视频,包含了这些细节
本文会经常更新,请阅读原文: https://blog.lindexi.com/post/WPF-%E7%82%B9%E5%87%BB%E6%8C%89%E9%92%AE%E6%97%B6%E6%9B%B4%E6%94%B9%E6%8C%89%E9%92%AE%E6%A0%B7%E5%BC%8F%E7%95%8C%E9%9D%A2%E6%95%88%E6%9E%9C%E7%9A%84-XAML-%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 。
- IntelliJ IDEA 缓存和索引的介绍及清理方法
- Node.js原理
- WCF系列教程之WCF消息交换模式之单项模式
- React Native调用Android相机图库
- IntelliJ IDEA 之 HelloWorld 项目创建及相关配置文件介绍
- 设置 IntelliJ IDEA 主题和字体的方法
- 修改 IntelliJ IDEA 模板注释中的 user 内容
- Android仿京东、天猫商品详情页
- C# checked和unchecked运算符
- 迭代子模式
- WCF系列教程之WCF中的会话
- SortedList<TKey,TValue> 和 SortedDictionary<TKey,TValue>
- Spring Boot入门
- React Native在Android平台运行gif的解决方法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法