WPF编程宝典使用C# 2008和.NET 3.5(第2版)样式

WPF编程宝典使用C# 2008和.NET 3.5(第2版)样式
WPF编程宝典使用C# 2008和.NET 3.5(第2版)样式

第 12 章

样 式

在第11章学习了WPF资源系统,使用资源可以在一个地方定义对象而在整个标记中使用它们。尽管可以使用资源存储各种对象,但是使用资源最常见的原因是通过它们保存样式。

样式是可以应用到元素的属性值的集合。WPF样式系统和HTML标记中的层叠样式表(cascading style sheet,CSS)标准扮演类似的角色。和CSS类似,通过WPF样式可以定义格式化特性集合,并且为了保证一致性,在整个应用程序中应用它们。同CSS一样,WPF样式也可以自动工作,指定具体的元素类型,并且通过元素树层叠起来。然而,WPF样式的功能更加强大,因为它们能够设置任何依赖项属性。这意味着可以使用它们标准化未格式化的特性,如控件的行为。WPF样式还支持触发器,当一个属性发生变化时可以通过触发器改变控件的样式(正如将要在本章中介绍的那样),并且能够使用模板重新定义控件的内置外观(将在第15章中介绍这些内容)。一旦学习了如何使用样式,就可以在所有的WPF应用程序中使用它们。

12.1 样式基础

在第11章中已学习过,资源具有几个重要的优点,包括简化标记和使应用程序更容易维护。那么样式有哪些优点呢?

为了理解适合使用样式的场合,分析一个简单的示例十分有帮助。设想需要标准化在窗口中使用的字体。最简单的方法是设置包容器窗口的字体属性。这些属性是在Control类中定义的,包括FontFamily属性、FontSize属性、FontWeight属性(用于粗体)、FontStyle属性(用于斜体)以及FontStretch属性(用于压缩的或扩展的变体)。幸运的是,这些属性值具有继承特性,当在窗口级别上设置这些属性时,在窗口中的所有元素都会使用相同的属性值,除非显式地覆盖它们。

注意:

属性值继承是依赖项属性提供的许多可选的特性之一。在第6章已介绍了依赖项属性。

现在考虑一种不同的情况,希望只为用户界面中的一部分锁定字体。如果能够在一个特定包容器中隔离这些元素(例如,它们都处于一个Grid面板或StackPanel面板中),可以使用本质上相同的方法,并设置包容器的字体属性。但问题并不总是这么简单。例如,可能希望使所有的按钮具有一致的字体和文本尺寸,并且使用和其他元素不同的字体设置。对于这种情况,就需要另外一种方法,在某个地方定义这些细节,并在所有应用它们的地方重用这些细节。

资源提供了一个解决方案,但是有些笨拙。因为在WPF中没有Font对象(只有与字体属性

第12章样式

相关的集合),所以需要定义几个相关的资源,如下所示:

Times New Roman

18

Bold

上面的标记为窗口添加了三个资源:第一个资源是FontFamily对象,该资源包含希望使用

的字体名称;第二个资源是存储数字18的double对象;第三个资源是一个枚举值FontWeight.Bold。假定已经将.NET名称空间系统映射到XAML名称空间前缀,如下所示:

提示:

当使用资源设置属性时,正确匹配属性类型是非常重要的。这时WPF使用类型转换器的

方式和直接设置一个特性值是不同的。例如,如果正在为元素设置FontFamily特性,可以使用

字符串“Times New Roman”,因为FontFamilyConverter转换器会创建所需要的FontFamily对

象。但如果试图使用字符串资源设置FontFamily属性,情况就不同了——这时,XAML解析器

会抛出一个异常。

一旦定义了所需要的资源,下一步是在元素中实际使用这些资源。因为在整个应用程序的

生命周期中,这些资源永远不会发生变化,所以使用静态资源比较合理,如下所示:

这个示例可以工作,并且它将字体细节移出了标记。但该示例也存在两个问题:

●(除了资源名称相似之外)没有明确指明这三个资源是相互关联的。这使维护应用程序变

得复杂。如果希望设置更多的字体属性,或者决定为不同类型的元素维护不同的字体

设置,这一问题会变得更加严重。

●使用资源设置属性的标记非常繁琐。实际上,还没有原来不使用资源时简明(直接为元

素定义字体属性)。

可以通过定义一个将所有字体细节捆绑在一起的自定义类(如FontSetting类),来解决第一

个问题。然后可以创建FontSetting对象资源,并在属性中使用它的各种属性。然而,这仍然需

要使用繁琐的资源——并且还需要做一些额外的工作。

样式对这一问题提供了非常好的解决方案。可以定义一个独立的包装所有希望设置的属性

的样式。如下所示:

289

WPF编程宝典——使用C# 2008和.NET 3.5(第2版)

290

上面的标记创建了一个独立的资源:一个System.Windows.Style对象。这个样式对象包含了一个Setter集合,该集合具有三个Setter对象,每个Setter对象用于一个希望设置的属性。每个Setter对象由两部分信息组成:希望进行设置的属性和希望为该属性应用的数值。和所有的资源一样,样式对象有一个键名,当需要时可以从集合中提取它。对于该示例,键名是BigFontButtonStyle(根据约定,用于样式的键名通常以“Style”结尾)。

每个WPF元素都可以使用一个样式(或者没有样式),样式通过元素的Style属性(该属性是在FrameworkElement类中定义的)嵌入到元素中。例如,使用上面创建的样式配置一个按钮,需要让按钮指向样式资源,如下所示:

当然,也可以通过代码设置样式。需要做的全部工作就是使用熟悉的FindResource( )方法,从最近的资源集合中提取样式。下面的代码为一个名称为cmd的Button对象设置样式:

cmdButton.Style = (Style)cmd.FindResource("BigFontButtonStyle");

图12-1显示的窗口中的两个按钮使用了BigFontButtonStyle样式。

图12-1 通过样式重用按钮设置

注意:

样式设置元素的初始外观,但是可以随意覆盖它们设置的这些特征。例如,如果应用了BigFontButtonStyle样式,并且显式地设置了FontSize属性,在按钮标签中的FontSize设置会覆盖样式。理想情况下,不应当依赖这种行为——而是应当创建更多的样式,从而可以在样式级别上设置尽可能多的细节。这样在将来调整用户界面时可以有更大的灵活性,使干扰降到最低。

样式系统增加了许多优点。不仅可以创建多组明显相关的属性设置,而且还可以使标记更加流线型,从而使应用这些设置更加容易。最佳方式是,可以应用样式而不用关心设置了哪些属性。在上一个示例中,字体设置被组织到一个名称为BigFontButtonStyle的样式中。如果以后决定大字体按钮还需要更多的内边距和外边距空间,也可以为Padding属性和Margin属性添加设置器。所有使用样式的按钮会自动采用新的样式设置。

第12章样式

Setters集合是Style类中最重要的属性。但并不是唯一的属性,在Style类中一共有5个重

要的属性,在本章会介绍这些属性。表12-1列出了这些属性。

表12-1 Style类的属性

属性描述

Setters 设置属性值以及自动关联事件处理程序的Setter对象或EventSetter对象的集合

Triggers 继承自TriggerBase类并且能够自动改变样式设置的对象的集合。例如,当另外一个属性改变时,或者当发生某个事件时,可以修改样式

Resources 希望用于样式的资源集合。例如,可能需要使用一个对象设置多个属性。这时,将对象作为资源创建,然后再在Setter对象中使用资源效率会更高效(而不是使用嵌套的标签创建对象,作为每个

Setter对象的一部分)

BasedOn 通过该属性可以创建继承自(并且可以有选择地进行重写)其他样式设置的更加复杂的样式

TargetType 该属性标识应用样式的元素的类型。通过该属性可以创建只影响特定类型元素的样式,并且还可以创建能够为恰当的元素类型自动执行动作的样式

现在,您已经看到了一个使用样式的基本示例,这为进一步深入分析样式模型做好了准备。12.1.1 创建样式对象

在上一个示例中,样式对象是在窗口级别定义的,之后在窗口的两按钮中重用该样式。尽

管这是一种常见的设计,但也不是唯一的选择。

如果希望创建更精细的目标样式,可以使用包容器的Resources集合定义样式,如StackPanel

面板或Grid面板。如果希望在应用程序中重用样式,可以使用应用程序的Resources集合定义

样式。这些是更常用的方法。

严格地讲,不需要同时使用样式和资源。例如,可以通过直接填充特定按钮的样式集合来

定义样式,如下所示:

上面的代码虽然可以工作,但显然不是很有用。因为现在无法在其他元素之间共享该样式。

如果只是使用样式设置一些属性(如本示例),就不值得使用这种方法,因为直接设置属性

更加容易。然而,如果正在使用样式的其他特性,并且只希望将它应用到单个元素,这一方法

有时会有用。例如,可以使用该方法为一个元素关联事件处理程序。通过该方法还可以修改部

分元素控件模板(对于这种情况,需要使用Setter.TargetName属性,在元素的内部为特定组件应

用一个设置器,如列表框中的滚动条按钮。有关该技术的更多内容将在第15章中学习)。

291

WPF编程宝典——使用C# 2008和.NET 3.5(第2版)

29212.1.2 设置属性

正如所介绍的,每个Style对象包装了一个Setter对象的集合。每个Setter对象设置元素的单个属性。唯一的限制是设置器只能改变依赖项属性——不能修改其他属性。

在某些情况下,不能使用简单的特性字符串设置属性值。例如,不能使用简单的字符串设置ImageBrush对象(如第11章中使用的显示平铺模式的图像画刷)。对于这种情况,可以使用熟悉的XAML技巧,使用嵌套的元素代替特性。下面是一个示例:

提示:

如果希望在多个样式中(或在同一样式的多个设置器中)重用相同的图像画刷,可以将其定义为资源,然后再在样式中使用资源。

为了标识希望设置的属性,需要提供类和属性的名称。然而,需要的类名不一定是定义属性的类名。也可以是继承了准备设置的属性的派生类。例如,考虑如下版本的BigFontButtonStyle 样式,它使用Button类的引用代替了Control类的引用:

如果将上面的示例(见图12-1)替换为这个样式,可以得到完全相同的结果。那么这两者之间到底有什么区别呢?对于这种情况,区别在于WPF对可能包含相同的FontFamily属性、FontSize属性以及FontWeight属性,但又不继承自Button类的类的处理方式。例如,如果为Label控件使用了该版本的BigFontButtonStyle样式,就没有效果。WPF简单地略过这三个属性,因为不会应用它们。但如果使用原来的那个样式,字体属性就会影响Label控件,因为Label 类继承自Control类。

提示:

WPF忽略属性而不应用它们的这一事实,意味着使用样式设置的属性,不必是在应用样式的元素中定义的属性。例如,如果设置ButtonBase.IsCancel属性,只有当为按钮设置样式时才会有效果。

在WPF中还存在这样一种情况,在元素框架层次中的多个位置定义了同一个属性。例如,在Control类和TextBlock类中都定义了全部的字体属性(如FontFamily属性)。如果创建应用到

第12章样式

TextBlock对象以及应用到继承自Control类的元素的样式,可以按如下方式创建标记:

然而,这样不会得到所期望的结果。问题是尽管Button.FontFamily属性和TextBlock.FontFamily属性是在它们各自的基类中分别定义的,但它们都引用同一个依赖项属性(换句话说,TextBlock.FontSizeProperty属性引用和Control.FontSizeProperty属性引用都指向同

一个DependencyProperty对象。在第6章已学习了这个问题)。所以,当使用这个样式时,WPF

设置FontFamily属性和FontSize属性两次。最后应用的设置(对于该示例,是10个单位大小的Arial字体)具有优先权,并且被同时应用到Button对象和TextBlock对象。尽管这个问题非常

明确,但是对于许多属性并不存在该问题,如果经常为不同的元素类型创建应用不同格式的样

式,分析是否会存在这一问题就显得很重要了。

还可以使用另外一种技巧来简化样式声明。如果所有的属性都准备用于相同的元素类型,

可以设置Style对象的TargetType属性,指定准备应用属性的类。例如,如果创建只应用于按

钮的样式,可以按如下方式创建样式:

这样做相对方便一些。正如后面所分析的,如果不使用样式键名,TargetType属性还可以

作为自动应用样式的快捷方式。

12.1.3 关联事件处理程序

属性设置器是所有样式中最常见的要素,但是也可以创建为事件关联特定事件处理程序的EventSetter对象的集合。下面是一个示例,为MouseEnter事件和MouseLeave事件关联事件处

理程序:

下面是事件处理代码:

private void element_MouseEnter(object sender, MouseEventArgs e)

{

((TextBlock)sender).Background = new

SolidColorBrush(Colors.LightGoldenrodYellow);

}

293

WPF编程宝典——使用C# 2008和.NET 3.5(第2版)

294

private void element_MouseLeave(object sender, MouseEventArgs e)

{

((TextBlock)sender).Background = null;

}

MouseEnter事件和MouseLeave事件使用直接事件路由,这意味着它们不在元素树中冒泡和隧道。如果希望为大量的元素应用鼠标悬停其上的效果(例如,当鼠标移动到元素上面时,希望改变元素的背景色),需要为每个元素添加MouseEnter和MouseLeave事件处理程序。基于样式的事件处理程序简化了这一任务。现在只需要应用单个样式,该样式包含了属性设置器和事件设置器:

Hover over me.

图12-2显示了该技术的一个简单演示程序,该

程序中有三个元素,其中两个元素使用了

MouseOverHighlightStyle样式。

在WPF中,事件设置器是一种很少使用的技

术。如果需要使用在此演示的功能,您可能更喜欢

使用事件触发器,它以声明的方式定义了所希望的

行为(并且不需要任何代码)。事件触发器是专门为

实现动画而设计的,当创建鼠标浮动效果时它们更

有用。

当处理使用冒泡路由策略的事件时,事件设置

器不是一个好选择。对于这种情况,在高层次的元

素上处理希望处理的事件通常更容易。例如,如果

希望将工具条上所有的按钮连接到同一个Click事

件处理程序,最好的方法是为包含所有按钮的Toolbar元素管理单个事件处理程序。对于这种情况,没有必要使用事件设置器。

提示:

在许多情况下,显式地定义所有的事件并完全避免使用事件设置器会更清晰。如果需要为几个元素连接同一个事件处理程序,可以手动进行。还可以使用在包容器级别上关联事件处理程序以及通过命令集中逻辑(第10章)这类技巧。

12.1.4 多层样式

尽管可以在许多不同的层次定义任意数量的样式,但是每个WPF元素一次只能使用一个样式对象。乍一看,这好像是一种限制。但由于可以使用属性值继承和样式继承特性,因此实际上不存在这种限制。

例如,设想希望为一组控件使用相同的字体,而又不想为每个控件应用相同的样式。对于这种情况,可以将它们放置到一个面板(或其他类型的包容器)中,并且设置包容器的样式。只

要设置了具有属性值继承特征的属性,这些属性值就会被传递到子元素。使用这种模型的属性

图12-2 使用样式处理MouseEnter

事件和MouseLeave事件

第12章样式

包括IsEnabled属性、IsVisible属性、Foreground属性以及所有的字体属性。

对于另外一些情况,可能希望在另外一个样式的基础上创建样式。可以通过为样式设置BasedOn特性,使用这种样式继承。例如,分析下面的两个样式:

第一个样式(BigFontButtonStyle)定义了三个字体属性。第二个样式(EmphasizedBigFont ButtonStyle)从BigFontButtonStyle样式获取这些属性设置,然后还提供了改变前景色和背景色

的画刷的更多属性。通过使用这种分成两部分的设计,可以只应用字体设置,也可以应用字体

设置和颜色设置的组合。这种设计还可以创建包含已经定义的字体和颜色细节(但是不一定必须

包含这两者)的更多样式。

注意:

可以使用BasedOn属性创建一条完整的样式继承链。唯一的规则是,如果两次设置了同一

个属性,最后的属性设置器(在继承链中最远的继承类中的设置器)会覆盖其他以前的定义。

图12-3显示了继承样式在一个简单窗口中的工作情况,该窗口使用了两个样式。

图12-3 基于另一个样式创建新样式

样式继承增加了复杂性

尽管乍一看样式继承好像非常方便,但是通常不值得增加这么多麻烦。因为样式继承和代

码继承存在相同的问题:关联性使应用程序更加脆弱。例如,如果使用上面演示的标记,就必

295

WPF编程宝典——使用C# 2008和.NET 3.5(第2版)

296须为两个样式保持相同的字体特征。如果假定改变了BigFontButtonStyle样式,EmphasizedBigFontButtonStyle样式也会随之改变——除非显式地增加更多设置器来覆盖继承而来的值。

在两个样式的示例中这个问题还比较简单,但是如果在更真实的应用程序中使用样式继承,这个问题就变得很重要了。通常,样式根据不同的内容类型和内容扮演的角色进行分类。例如,一个销售应用程序可以包括以下样式:ProductTitleStyle样式、ProductTextStyle样式、HightlightQuoteStyle样式、NavigationButtonStyle样式等。如果ProductTitleStyle样式基于ProductTextStyle样式(可能因为二者共享相同的字体),当以后为ProductTextStyle样式应用设置,而又不想将这些设置应用到ProductTitleStyle样式时(如不同的外边距),就会遇到麻烦。对于这种情况,必须在ProductTextStyle样式中定义设置,并在ProductTitleStyle样式中显式地覆盖这些设置。最后,问题会变得更加复杂,并且实际上很少会使用这种样式。

除非有特殊原因要求一个样式继承自另外一个样式(例如,第二个样式是第一样式的特例,并且只改变继承来的大量设置之外的几个特征),否则不要使用样式继承。

12.1.5 通过类型自动应用样式

到目前为止,已经介绍了如何创建具有名称的样式以及如何在标记中引用它们。但还有另外一种方法,即可以为特定类型的元素自动应用样式。

这一工作非常简单。只需要设置TargetType属性以指定合适的类型(如前所述),并完全忽略键名。当这样做时,WPF实际上是隐式地使用类型标记扩展设置键名,如下所示:

x:Key="{x:Type Button}"

现在样式被自动应用到整个元素树中的所有按钮上。例如,如果在窗口中以这种方式定义了一个样式,它会被应用到窗口中的所有按钮上(除非有一个更特殊的样式替换了该样式)。

下面是一个示例,该示例中的窗口为了得到和图12-1中显示的相同效果,自动设置按钮样式:

Normal Content.

More normal Content.

在这个示例中,中间的按钮显式地替换了样式。但是该按钮并没有为自己提供一个新的样式,而是将Style属性设置为null值,这样有效地删除了样式。

尽管自动样式非常方便,但是它们会让程序变得复杂。下面是几条原因:

第12章样式

●在具有许多样式和多层样式的复杂窗口中,跟踪是否通过属性值继承或通过样式设置

了某个特定属性有些困难(如果是通过样式设置的,那么是通过哪个样式设置的呢?)。

因此,如果希望改变一些简单细节,需要查看整个窗口的全部标记。

●窗口中的格式化操作通常在开始时十分普通,并且会逐渐变得越来越详细。如果刚开

始为窗口应用了自动样式,在许多地方可能需要使用显式的样式覆盖自动样式。这会

使整个设计变得复杂。为每个希望的格式化特征组合创建命名的样式,并根据名称应

用它们更直观。

●再比如,如果为TextBlock元素创建一个自动样式,会同时修改使用TextBlock元素的

其他控件(如模板驱动的ListBox控件)。

为了避免这一问题,最好果断地使用自动样式。例如,可以在以下情况下使用自动样式:

为了使按钮边界和其内容之间具有一致的内边距,或者要控制特定包容器中而不是整个窗口中

文本框的外边距设置。

12.2 触发器

WPF中的一个主题是尽可能扩展声明式代码的功能。当使用样式、资源或者数据绑定时,

将会发现即使不使用代码,声明式代码也能够完成很多事情。

触发器就是实现这种功能的另一个例子。通过触发器,可以使简单的样式改变自动化,而

原来这需要使用样板事件处理逻辑。例如,当一个属性发生变化后您可以进行响应,并自动调

整样式。

触发器通过Style.Triggers集合连接到样式。每个样式都可以有任意多个触发器,并且每个

触发器都是继承自System.Windows.TriggerBase基类的类的实例。WPF提供的继承自System.Windows.TriggerBase基类的类如表12-2中所列。

通过使用FrameworkElement.Triggers集合,可以直接为元素应用触发器,而不需要创建样

式。但这存在一个相当大的缺陷。元素的Triggers集合只支持事件触发器(对于这个限制没有技

术上的原因,只是因为WPF团队没有时间来实现支持其他类型触发器的特性,并且在将来的

版本中可能会包含支持其他类型触发器的特性)。

表12-2 继承自TriggerBase的类

名称描述

Trigger 这是最简单的一种触发器。它监测依赖项属性的变化,然后使用设置器改变样式

MultiTrigger 和触发器类似,但是它联合了几个条件。只有满足了所有这些条件后,触发器才会生效DataTrigger 这种触发器使用数据绑定。它和Trigger类似,只是监视所有绑定数据的变化

MultiDataTrigger 联合多个数据触发器

EventTrigger 这是最复杂的触发器。当事件发生时,它通过动画进行响应

12.2.1 简单触发器

可以为任何依赖项属性关联简单触发器。例如,可以通过响应Control类的IsFocused属性、

297

WPF编程宝典——使用C# 2008和.NET 3.5(第2版)

298IsMouseOver属性以及IsPressed属性的变化,创建鼠标浮动效果和焦点效果。

每个简单触发器都指定了要监视的属性,以及正在监视的属性值。当该属性值出现时,就会应用存储在Trigger.Setters集合中的设置器(但不能使用更复杂的触发器逻辑,例如,比较一个值,查看其是否落入某个范围,或执行一个计算等等。对于这些情况,最好使用事件处理程序)。

下面的触发器等待按钮获取键盘焦点,当获取焦点时会将其前景色设置为深红色:

触发器的优点是不需要为翻转它们而编写任何逻辑。只要触发器被去活,元素就会恢复到它的正常外观。例如,在该示例中,这意味着只要用户让按钮失去焦点,按钮就会恢复为原来的灰色背景。

注意:

为了理解触发器的工作原理,需要记住在第6章中学习过的依赖项属性系统。本质上,触发器是众多覆盖从依赖项属性返回值的属性提供者中的一个。但原始的属性值(不管是在本地设置的还是通过样式设置的)仍会保留。只要触发器被去活,触发器之前的属性值就会再次可用。

可以创建一次应用到相同元素的多个触发器。如果这些触发器设置不同的属性,这种情况就不会出现混乱。如果多个触发器修改同一个属性,那么最终是最后的触发器有效。

例如,分析下面的触发器,这些触发器根据控件是否具有焦点、鼠标是否悬停在控件上,以及是否单击了控件,对控件进行修改:

第12章样式

显然,鼠标可能悬停在一个具有焦点的控件之上。这不会出现问题,因为这两个触发器修

改不同的属性。但是如果单击按钮,就有两个不同的触发器试图设置前景色。现在针对Button.IsPressed属性的触发器胜出,因为它是最后一个触发器。这与哪个触发器首先发生并没

有关系——例如,WPF不关心是否在单击按钮之前按钮获得了焦点。触发器在标记中的排列顺

序完全决定了最终的结果。

注意:

在这个示例中,要得到一个外观较好的按钮,触发器不能满足全部需求。还要受到按钮控

件模板的限制。当在这种层次自定义元素时,为了得到最佳结果,需要使用控件模板。然而,

控件模板不能代替触发器——实际上,控件模板经常使用触发器,这样就可以完全自定义控件,

并且可以响应鼠标悬停事件、单击事件以及其他事件来改变它们可视化外观的某些方面。

如果希望创建只有当几个条件都为真时才激活的触发器,可以使用MultiTrigger触发器。

它提供了一个Conditions集合,可以通过该集合定义一系列属性和值的组合。在下面的示例中,

只有当按钮具有焦点并且鼠标悬停在该按钮上时,才会应用格式化信息:

对于上面这种情况,不必关心声明条件的顺序,因为在改变背景色之前,这些条件都必须

保持为真。

12.2.2 事件触发器

普通的触发器为触发操作需要等待属性值发生变化,而事件触发器为了被引发,需要等待

特定的事件。您可能会认为这时应使用设置器来改变元素,但是情况并非如此。因为事件触发

器会要求用户提供修改控件的一系列动作,而这些动作被用于申请一个动画。

尽管直到第21章才会学习动画,但是可以通过一个基本的示例来了解其基本概念。下面的

事件触发器等待MouseEnter事件,然后连续改变按钮的FontSize属性从而形成动画效果,本例

中在0.2秒的时间内将字体放大到22个单位:

有趣的是,当依赖项属性等于一个特定值时也可以执行动画。如果没有合适的事件可供使

用而又希望执行一个动画时,这是非常有用的。

为了使用这一技术,需要使用上一小节中介绍的属性触发器。技巧是不为属性触发器提供

任何Setter对象。而是设置Trigger.EnterActions和Trigger.ExitActions属性。这两个属性都有一

个动作集合,例如,开始一个动画的BeginStoryboard动作。当属性到达指定的值时,执行EnterActions,而当属性离开指定的值时,执行ExitActions。

在第21章将会学习使用事件触发器和属性触发器加载动画的更多内容。

12.3 结束语

在本章学习了如何定义具有名称的属性值设置(样式),以及如何很容易地将它们应用到合

适的元素。

样式是WPF支持的许多特性的关键要素。例如,通过样式可以为许多控件应用新的控件

模板;根据当前的系统主题使用不同的格式;动态改变应用程序的皮肤;以及使用动画增强元

素。在后面的章节中将会学习这些技术的有关内容(在第15章,将会分析控件模板、主题和应

用程序皮肤;在第21章,将深入分析动画)。但是在这之前,首先需要介绍WPF的其他核心主

题:丰富的二维绘图特性。

301

wpf自定义slider控件

自定义Slider控件 最终效果: 界面: