注:一下内容及代码基本来自Charles Petzold著,蔡学庸译,电子工业出版社出版的《Windows Presentation Foundation 程序设计指南》一书
1. Style概述
- Style是Property值的collection,这些Style的property值可以用在element上,Style可以一定程度弥补XAML无法使用循环语句的缺点。
- 最重要的Property是, 类型为,这是的集合,
- 是一个抽象类的子类有和
因此,最基本的Style格式是:
< Style ... > < Setter ... /> < EventSetter ... /> < Setter ... /> </ Style > 2. Style属性之一:
3. 关于对Property设置的优先级
- 如有一个仅供某个element使用的Style(这样设置用处不大,仅为举例)
代码 < Button xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" HorizontalAlignment ="Center" VerticalAlignment ="Center" Foreground ="Red" > < Button.Style > < Style > < Setter Property ="Button.FontSize" Value ="18pt" /> < Setter Property ="Control.Foreground" Value ="Blue" /> </ Style > </ Button.Style > Button with Local Style </ Button > - 如果像这样,在element中和Style中都对Foreground做设定,那么“局部设定”的优先级优先于Style的设定
- 而Style的优先级又高于从visual tree 继承来的Property设定
4. Style作为Resource使用
如下,一个由Button控件和TextBlock element共用的Style
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style x:Key ="normal" > < Setter Property ="Control.FontSize" Value ="24" /> < Setter Property ="Control.Foreground" Value ="Blue" /> < Setter Property ="Control.HorizontalAlignment" Value ="Center" /> < Setter Property ="Control.Margin" Value ="24" /> < Setter Property ="Control.Padding" Value ="20, 10, 20, 10" /> </ Style > </ StackPanel.Resources > < Button Style ="{StaticResource normal}" > Button on top of the stack </ Button > < TextBlock Style ="{StaticResource normal}" > TextBlock in the middle of the stack </ TextBlock > < Button Style ="{StaticResource normal}" > Button on the bottom of the stack </ Button > </ StackPanel > 也可以为某个独有的Property定义一个Setter。例如:
< Setter Property ="Button.IsDefault" Value ="true" /> 只会设置Button的IsDefault,因为TextBlock不具有此属性
5. 具有相同key的style
可以在多个resource中使用相同的key来定义stlye。对于某个特定的element来说,它将沿视觉树往上搜索,采用第一个和key关联的style。
代码 < Grid xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < Grid.Resources > < Style TargetType ="{ x:Type Button}" > < Setter Property ="Control.FontSize" Value ="24" /> < Setter Property ="Control.Foreground" Value ="Blue" /> </ Style > </ Grid.Resources > < StackPanel > < StackPanel.Resources > < Style TargetType ="{ x:Type Button}" > < Setter Property ="Control.Foreground" Value ="Red" /> </ Style > </ StackPanel.Resources > < Button > Button Number 1 </ Button > < Button > Button Number 2 </ Button > < Button > Button Number 3 </ Button > </ StackPanel > </ Grid > 说明:
这些按钮会使用在StackPanel中定义的style,前景色为红色,字体为默认值。
使用相同名称的style不会覆盖某些property的同时还去保留另外一些。
6. Style属性之二:Resource
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style x:Key ="normal" > < Style.Resources > < LinearGradientBrush x:Key ="gradbrush" StartPoint ="1,0" EndPoint ="1,1" > < LinearGradientBrush.GradientStops > < GradientStop Color ="LightBlue" Offset ="0" /> < GradientStop Color ="Aquamarine" Offset ="1" /> </ LinearGradientBrush.GradientStops > </ LinearGradientBrush > </ Style.Resources > < Setter Property ="Control.FontSize" Value ="24" /> < Setter Property ="Control.HorizontalAlignment" Value ="Center" /> < Setter Property ="Control.Margin" Value ="24" /> < Setter Property ="Control.Background" Value ="{StaticResource gradbrush}" /> </ Style > </ StackPanel.Resources > < Button Style ="{StaticResource normal}" > Button Number 1 </ Button > < Button Style ="{StaticResource normal}" > Button Number 2 </ Button > < Button Style ="{StaticResource normal}" > Button Number 3 </ Button > </ StackPanel > 以上代码,如果不使用Resource,就要将value打散:
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style x:Key ="normal" > < Setter Property ="Control.FontSize" Value ="24" /> < Setter Property ="Control.HorizontalAlignment" Value ="Center" /> < Setter Property ="Control.Margin" Value ="24" /> < Setter Property ="Control.Background" > < Setter.Value > < LinearGradientBrush StartPoint ="1,0" EndPoint ="1,1" > < LinearGradientBrush.GradientStops > < GradientStop Color ="LightBlue" Offset ="0" /> < GradientStop Color ="Aquamarine" Offset ="1" /> </ LinearGradientBrush.GradientStops > </ LinearGradientBrush > </ Setter.Value > </ Setter > </ Style > </ StackPanel.Resources > < Button Style ="{StaticResource normal}" > Button Number 1 </ Button > < Button Style ="{StaticResource normal}" > Button Number 2 </ Button > < Button Style ="{StaticResource normal}" > Button Number 3 </ Button > </ StackPanel > 7. Style属性之三:TargetType
TargetType,指定此style被用到何种类型的element上。
被应用的element必须“类型完全符合”,例如,如果TargetType为Control,则此Sytle不会采用到Button或者label控件上。
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style TargetType ="{ x:Type Button}" > < Setter Property ="FontSize" Value ="24" /> < Setter Property ="Foreground" Value ="Blue" /> </ Style > < Style TargetType ="{ x:Type TextBlock}" > < Setter Property ="Foreground" Value ="Red" /> </ Style > </ StackPanel.Resources > < Button > Button with Text Content </ Button > < TextBlock > TextBlock Text </ TextBlock > < Button > < TextBlock > Button with TextBlock Content </ TextBlock > </ Button > </ StackPanel > 说明:
第一个element:<Button>采用第一个style。前景色为蓝色,字体为24
第二个element:<TextBlock>采用第二个style。前景色为红色,字体为默认值第三个element:<Button>中的<TextBlock>,前景色会采用第二个style(红色),而字体为24。原因是Sytle比“property继承”优先级高,所以前景色用第二个style,但是由于该style中没有对字体设定,所以会继承视觉树上其父亲的此property。
注:
- 可以在指定TargertType的同时指定Key
- 只要指定了TargertType,特定Type的element就一定会使用特定的style(同一个TargetType的style,有的style有key,有的style没有key,那么没有指定key的element也会用没有指定key的style)
- 如果多个style被定义在同一个resource section,他们的key不可以重复
8. Style属性之四:BasedOn
BasedOn有类似“继承”的作用,可以修改已有的Style:
- baseon的时候,TargetType也可以用继承
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style TargetType ="{x:Type Control}" > < Setter Property ="Control.FontSize" Value ="24" /> < Setter Property ="Control.Foreground" Value ="Blue" /> < Setter Property ="Control.HorizontalAlignment" Value ="Center" /> < Setter Property ="Control.Margin" Value ="24" /> </ Style > < Style TargetType ="{x:Type Button}" BasedOn ="{StaticResource {x:Type Control}}" > < Setter Property ="Control.Foreground" Value ="Red" /> </ Style > < Style TargetType ="{x:Type Label}" BasedOn ="{StaticResource {x:Type Control}}" > < Setter Property ="Control.Foreground" Value ="Green" /> </ Style > < Style TargetType ="{x:Type TextBox}" BasedOn ="{StaticResource {x:Type Control}}" > </ Style > </ StackPanel.Resources > < Button > Button Control </ Button > < Label > Label Control </ Label > < TextBox > TextBox Control </ TextBox > </ StackPanel > 9. 使用Style的内在约束
- 不能定义没有dependency property支持的值,例如不能指定一个stackpanel所具有的孩子个数(应该使用Template)
- setter不支持派生自Visual或ContentElement的Value(例如将一张图片设为使用该style的Button的Content)。因为在特定的style中引用到的任何对象,都只会被创建一次,成为该style的一部分,所以会被使用该style的element所共有。
10. 将数据绑定应用到Style
- Setter 中的 Value属性可以是一个数据绑定
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style TargetType ="{x:Type Button}" > < Setter Property ="FontSize" Value ="{Binding ElementName=scroll, Path=Value}" /> < Setter Property ="HorizontalAlignment" Value ="Center" /> < Setter Property ="Margin" Value ="24" /> </ Style > </ StackPanel.Resources > < ScrollBar Name ="scroll" Orientation ="Horizontal" Margin ="24" Minimum ="11" Maximum ="100" Value ="24" /> < Button > Button Number 1 </ Button > < Button > Button Number 2 </ Button > < Button > Button Number 3 </ Button > </ StackPanel > 以上Button的Fontsize的Value被绑定到一个名为“scroll”的ScrollBar的Value Property上。
< Setter Property ="X2" Value ="{Binding RelativeSource={RelativeSource self}, Path=X1}" /> 以上代码将x2属性的值绑定为和x1一样。
11. Style中的事件处理 :
EventerSetter和Setter级别相同,用来为特定的routed事件设定事件处理函数
代码 < Window.Resources > < Style TargetType ="{x:Type Button}" > < Setter Property ="FontSize" Value ="24" /> < Setter Property ="HorizontalAlignment" Value ="Center" /> < Setter Property ="Margin" Value ="24" /> < EventSetter Event ="Click" Handler ="ButtonOnClick" /> </ Style > </ Window.Resources > < StackPanel > < Button > Button Number 1 </ Button > < Button > Button Number 2 </ Button > < Button > Button Number 3 </ Button > </ StackPanel > </ Window > 12. Style的第五个属性:Triggers
- Triggers设定element或控件如何响应“此element property 的改变”,或者“数据绑定的改变”,或者“事件的发生”
- Setter和Trigger都涉及“要被设定的”property。Setter是在element第一次创建时进行property设定,Trigger只有当某些事情发生时,才会去设定property(也就是说某些事情触发使得此property改变)
- Triggers属性的类型是TriggerCollection,这是TriggerBase对象的collection, 派生自TriggerBase类型的Trigger有五种:
- System.Windows..::.TriggerBase (主要用于图形动画)
一下依次介绍除EventTrigger之外的各种Trigger
Trigger是最常见的,用来指定control或element应该如何响应特定的propertyd的改变,这些property常常都涉及到用户的输入,例如IsMouseOver property
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style x:Key ="normal" > < Setter Property ="Control.FontSize" Value ="24" /> < Setter Property ="Control.HorizontalAlignment" Value ="Center" /> < Setter Property ="Control.Margin" Value ="24" /> < Style.Triggers > < Trigger Property ="Control.IsMouseOver" Value ="true" > < Setter Property ="Control.FontStyle" Value ="Italic" /> < Setter Property ="Control.Foreground" Value ="Blue" /> </ Trigger > < Trigger Property ="Button.IsPressed" Value ="true" > < Setter Property ="Control.Foreground" Value ="Red" /> </ Trigger > </ Style.Triggers > </ Style > </ StackPanel.Resources > < Button Style ="{StaticResource normal}" > Button Number 1 </ Button > < Button Style ="{StaticResource normal}" > Button Number 2 </ Button > < Button Style ="{StaticResource normal}" > Button Number 3 </ Button > </ StackPanel > 说明:
1. 当property的值被改变回去时,被Trigger修改的Property也会恢复他们原始的状态
2. 以上两个Trigger的次序对效果有所影响,如果两个Trigger对相同的property做设定,后一个会覆盖前一个。如果以上两个Trigger对调,文字不会变成红色,因为press的时候,IsMouseOver肯定是为True的。
MultiTrigger只有在两个或者多个事件都成立时才会被触发
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style x:Key ="normal" > < Setter Property ="Control.FontSize" Value ="24" /> < Setter Property ="Control.HorizontalAlignment" Value ="Center" /> < Setter Property ="Control.Margin" Value ="24" /> < Style.Triggers > < Trigger Property ="Button.IsPressed" Value ="True" > < Setter Property ="Control.Foreground" Value ="Red" /> </ Trigger > < MultiTrigger > < MultiTrigger.Conditions > < Condition Property ="Control.IsMouseOver" Value ="True" /> < Condition Property ="Button.IsPressed" Value ="False" /> </ MultiTrigger.Conditions > < Setter Property ="Control.FontStyle" Value ="Italic" /> < Setter Property ="Control.Foreground" Value ="Blue" /> </ MultiTrigger > </ Style.Triggers > </ Style > </ StackPanel.Resources > < Button Style ="{StaticResource normal}" > Button Number 1 </ Button > < Button Style ="{StaticResource normal}" > Button Number 2 </ Button > < Button Style ="{StaticResource normal}" > Button Number 3 </ Button > </ StackPanel > 鼠标移动到按钮上:文字蓝色
点击按钮:文字红色
鼠标移动到按钮上方且按钮没有被按下:文字斜体
DataTrigger与Trigger类似,只是用Binding取代了Property,Binding通常引用到另一个element。
代码 < StackPanel xmlns ="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x ="http://schemas.microsoft.com/winfx/2006/xaml" > < StackPanel.Resources > < Style TargetType ="{x:Type Button}" > < Setter Property ="FontSize" Value ="24" /> < Setter Property ="HorizontalAlignment" Value ="Center" /> < Setter Property ="Margin" Value ="24" /> < Style.Triggers > < DataTrigger Binding ="{Binding ElementName=txtbox, Path=Text.Length}" Value ="0" > < Setter Property ="IsEnabled" Value ="False" /> </ DataTrigger > </ Style.Triggers > </ Style > </ StackPanel.Resources > < TextBox Name ="txtbox" HorizontalAlignment ="Center" Width ="2in" Margin ="24" /> < Button > Button Number 1 </ Button > < Button > Button Number 2 </ Button > < Button > Button Number 3 </ Button > </ StackPanel > 以上设定,只有当TextBox中的字符数不为0,Button才会Enable
只有在多个绑定条件都成立时,才会触发事件
代码 < StackPanel.Resources > < Style TargetType ="{x:Type CheckBox}" > < Setter Property ="HorizontalAlignment" Value ="Center" /> < Setter Property ="Margin" Value ="12" /> </ Style > < Style TargetType ="{x:Type Button}" > < Setter Property ="FontSize" Value ="24" /> < Setter Property ="HorizontalAlignment" Value ="Center" /> < Setter Property ="Margin" Value ="12" /> < Setter Property ="IsEnabled" Value ="False" /> < Style.Triggers > < MultiDataTrigger > < MultiDataTrigger.Conditions > < Condition Binding ="{Binding ElementName=chkbox1, Path=IsChecked}" Value ="True" /> < Condition Binding ="{Binding ElementName=chkbox2, Path=IsChecked}" Value ="True" /> </ MultiDataTrigger.Conditions > < Setter Property ="IsEnabled" Value ="True" /> </ MultiDataTrigger > </ Style.Triggers > </ Style > </ StackPanel.Resources > < CheckBox Name ="chkbox1" > Check 1 </ CheckBox > < CheckBox Name ="chkbox2" > Check 2 </ CheckBox > < Button > Button Number 1 </ Button > < Button > Button Number 2 </ Button > < Button > Button Number 3 </ Button > </ StackPanel > 以上设定,只有两个checkbox都被选中,才会时按钮enable