概念
样式是可以应用于元素的一系列属性值的集合。
样式期望使用一份xaml代码来设置一系列元素的细节,比如内外边距,字体颜色等。
样式类似于CSS但是要比CSS更加强大,强大之处在于:
- 可以设置依赖项属性,使其可以控制控件行为
- 支持触发器
- 可以使用模板重新定义控件的内置外观
定义和设置样式
定义样式
<Window.Resources>
<Style x:Key="BigFontButtonStyle">
<Setter Property="Control.FontFamily" Value="Times New Roman"/>
<Setter Property="Control.FontSize" Value="18"/>
<Setter Property="Control.FontWeight" Value="Bold"/>
</Style>
</Window.Resources>
设置样式
<Button x:Name="btn" Style="{StaticResource BigFontButtonStyle}">Test Button</Button>
通过代码设置样式
btn.Style = (Style)window.FindResource("BigFontButtonStyle");
设置样式
在上面最简单的例子中我们看到了Setter的使用方法,这一节我们详细看一下:
- Setter只能使用在依赖项属性上
- Setter不是只能设置字符串,也可以设置对象,需要用到下面的语法
<Style x:Key="TestStyle"> <Setter Property="Control.Background"> <Setter.Value> <SolidColorBrush Color="Aquamarine"/> </Setter.Value> </Setter> </Style>
- 如果上面的例子中这个画刷要被多次使用,可以定义为资源,下面的代码体现了这样的修改
<SolidColorBrush x:Key="TestBrush" Color="Aquamarine"/> <Style x:Key="TestStyle"> <Setter Property="Control.Background" Value="{StaticResource TestBrush}"/> </Style>
TargetType
我们在设置样式的时候,我们发现在写Property的时候,都是写Control.Background,这是告诉样式我们这个样式应用于Control,但是我们写多少个Setter就要写多少次Control,为了简化写法,我们推荐将对于一种类型控件的设置写到一个样式中并为其指定TargetType,这样可以简化代码
<Style TargetType="Button">
<Setter Property="Background" Value="{StaticResource TestBrush}"/>
</Style>
如果在Style的属性上指定了TargetType,就不需要在Property中再每一次都指定应用于什么元素了。
样式的继承
样式的继承允许我们从一个样式中继承所有的设置,并且针对性的修改几个属性,需要使用BasedOn语法,我们看看其用法
<SolidColorBrush x:Key="TestBrush" Color="Aquamarine"/>
<Style x:Key="TestStyle">
<Setter Property="Control.Background" Value="{StaticResource TestBrush}"/>
<Setter Property="Control.FontWeight" Value="Bold"/>
</Style>
<Style x:Key="BaseStyle" BasedOn="{StaticResource TestStyle}">
<Setter Property="Control.FontWeight" Value="Normal"/>
</Style>
如果按钮使用了 BaseStyle 其底色是从TestStyle继承而来的,但是其FontWeight是覆盖之后的Normal
我们需要注意的是:只在有大量样式相同,只有一点点不同需要改变的时候使用继承语法,否则继承语法带来的样式复杂性将使人得不偿失。慎用。
应用范围
之前我们都是使用x:Key来声明一个样式,如果不使用它,我们就需要注意样式的应用范围,我们记住下面两种原则
- 完全不指明的样式不会被应用
<!--不会被应用--> <Style> <Setter Property="Control.Background" Value="Red"/> </Style>
- 只指明TargetType的样式为该控件在全局的默认样式
<!--会被应用到有效范围内的所有按钮--> <Style TargetType="Button"> <Setter Property="Control.Background" Value="Red"/> </Style>
然而,我们需要十分谨慎的使用全局默认样式,主要因为在实际的开发中,我们经常会嵌套控件(使用模板),在对于某个控件的样式设置可能会影响很多不需要影响的控件,而且,在开发过程中,突然出现的不知道来自于哪里的默认样式常常会让我们十分抓狂,所以除非是明确的全局字体等,不要使用基于控件类型的全局的样式。
样式清除
给样式赋值 {x:Null} 可以清除样式
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Control.Background" Value="Red"/>
</Style>
</Window.Resources>
<StackPanel>
<Button x:Name="btn" Style="{x:Null}">Test Button</Button>
</StackPanel>
上面例子中,按钮使用 {x:Null} 清除了全局按钮背景色带来的影响。
Style类的属性
不知不觉中,我们已经学了样式的好几个属性,Setter 用于设置样式,TargetType用于设置样式应用的控件类型以及默认控件样式,BasedOn用于继承,而样式不止这几个属性,还有下面几个
由于触发器比较重要所以我们下一节用一节的篇幅学习Style的另一个属性,Trigger,触发器。
我们先看Style的另外一些知识。
关联事件处理程序
我们之前讲了Setter,还有一种EventSetter,用于绑定事件。事先说明,这种用法使用的很少,不感兴趣的可以直接跳过。
<Window.Resources>
<Style TargetType="Button" x:Key="ClickBtnStyle">
<EventSetter Event="Click" Handler="element_Click"/>
</Style>
</Window.Resources>
<StackPanel>
<Button x:Name="btn" Style="{StaticResource ClickBtnStyle}">Test Button</Button>
</StackPanel>
使用了ClickBtnStyle样式的按钮都会相应element_Click定义的点击事件。这种写法有一个限制就是必须写在窗体或者控件中,无法写在资源字典中(因为它必须连接事件处理程序,所以必须指明x:Class,但是资源字典没有这个属性)
我们在实际的开发中应该尽量不使用这种技术,这里仅做了解即可。
样式与共享依赖属性
我们在之前讲依赖项属性的时候提到过界面上Button和TextBlock虽然各自有各自的FontFamily属性,但是本质上是同一个依赖项属性的问题(因为使用了共享的依赖项属性),所以我们看下面的例子
<Style>
<Setter Property="Button.FontFamily" Value="Times New Roman"/>
<Setter Property="TextBlock.FontFamily" Value="Arial"/>
</Style>
在这个例子中我们期望把Button和TextBlock设置为不同的字体,但是事实上,两者的字体都被设置成了Arial,我们应当知道这个问题,在遇到问题的时候就可以节省很多解决问题的时间。
特别注意
- 不仅仅是在样式中可以使用触发器,在FrameworkElement中也有Triggers集合,但是并不常用,因为这里面只能支持事件触发器
- 如果我们使用Background进行测试,我们经常会发现一些属性的表现与我们设想的并不一致,这是因为按钮的行为不止受样式的影响,还受到模板的影响,两者相结合我们才能真正的完全控制一个控件。再之后我们学习模板的时候我们还会慢慢理解这种关系