目前为止,我们已经看到样式,作为一个Setter元素的集合。当应用一个样式时,在Setter元素中描述的设置不会无条件地应用(除非复写每一个设置的实例)。另一方面,触发器是一种在条件中包装了一个或更多Setter元素的方式,如果条件为真,相应地Setter元素会被执行,而条件为false的时候,属性值返回预先触发的值。
WPF伴随着3种你可以在一个触发器条件中检查的事情一起发生,依赖属性,.NET属性,.NET事件。头两个直接改变基于条件的值,如我所描述的;而最后一个,一个事件触发器,被激活于一个事件发生并开始一个引起属性变化的动画。
5.6.1属性触发器
触发器最简单的形式是属性触发器,它将监视一个依赖属性得到一个确定的值。例如,如果我们想要使一个按钮当用户移动鼠标到这个按钮上的时候变为黄色,我们可以这么做通过注释IsMouseOver属性是否有一个true值,正如示例5-24所示。
示例5-24
一些触发器在Style.Triggers元素下被分组。在这种情形中,我们添加了一个Trigger元素到button的样式中。当isMouseOver属性为true时,这个按钮的Background值会被设为黄色,正如图5-8所示。
在图5-8中,你会注意到,只有鼠标当前所在位置的按钮会被设置黄色背景,即使其他的按钮曾经在鼠标下也没有变色。当触发器不再为真时,没有必要着急于使这个属性回到原先,例如,监视IsMouseOver为false。WPF依赖属性系统监视到属性触发器变为无效的时,就会回复到原先设置的值。
图5-8
可以设置属性触发器来监视控件上的任何一个依赖属性,到样式的定位,以及设置控件上的任何依赖属性到当条件为true的时候。实际上,你可以使用一个单独的触发器来设置你喜欢的多重属性,如示例5-25。
示例5-25
在示例5-25中,我们设置背景为黄色和字体样式为italic,当鼠标在按钮上的时候。
在触发器中不允许设置Style属性自身。如果你尝试这么做,会的到下面的错误:
A Style object is not allowed to affect the Style property of the object to which it applies.
这样做是有意义的。由于是Style在设置属性,而且当触发器不再为true时,还有参与将这个属性改为“未设置”,
这看上去有点像转换你的幽默故事在你 在你着陆之前在开始奋力一跳之后。
5.6.2多重触发器
当你可以设置任意多的属性在你的属性触发器的时候,在一个样式中可能存在多于一个的触发器。当对Style.Trigger下的元素进行分组的时候,多重触发器表现为每个触发器各自独立。
例如,我们可以更新代码为了鼠标移动到我们的一个按钮之上,这个按钮将被设为黄色,而且一旦该按钮获得焦点(tab或箭头移动到焦点范围内),这个按钮会被设为绿色,如示例5-26所示。图5-9显示了这样的结果:一个单元格得到了焦点,另一个则有鼠标盘旋。
示例5-26
图5-9
入如果多重触发器设置了相同的属性,会采取最后的一个。例如,在图5-9中,如果一个按钮得到焦点,同时鼠标盘旋其上,它的背景会变为绿色,因为IsFocused触发器是触发器列表的最后一个。
5.6.3多重条件的属性触发器
如果你想要检查多于一个的属性在一个触发器条件激活前,例如,鼠标盘旋在一个按钮上,按钮内容为空。你可以联合这些条件在一个多重条件属性触发器中,如示例5-27所示。
示例5-27
多重条件属性触发器检查所有属性的值是否被明确指定,而不仅是其中的一个。这里,我们监视按钮盘旋以及内容不为空*,反映了游戏的逻辑:只有点击在一个空的单元格才认为是有效的一次移动。
*null值通过xaml扩展标记设置,你可以从附录A中获取更多信息。
图5-10显示了当鼠标盘旋在一个空单元格上,黄色高亮显示,;图5-11显示了当鼠标盘旋在一个非空单元格上,不具备黄色高亮显示。
图5-10
图5-11
当一个用户与显示你的程序状态的控件进行交互时,属性触发器时非常值得关注的。然而,我们也相要关注程序的状态何时改变,正如一个特定的玩家走了一步,和相应地更新样式的设置。为了这点,我们有了数据触发器。
5.6.4数据触发器
不同于属性触发器——只检查WPF依赖属性,数据触发器可以检查任何任何旧有地.NET对象属性。当属性触发器普遍用于检查WPF可视化元素地属性时,数据触发器通常用于检查用于内容的非可视化对象的属性,正如示例5-28的PlayerMove对象。
示例5-28
DataTrigger元素使用在Style.Trigger元素中,正如属性触发器一样,这里可以有多于一个是活动的在任何时刻。一个属性触发器在显示内容的可视化元素的属性上操作,而数据触发器在其自身进行操作。这我们这种情形下,每一个单元格的内容都是一个PlayerMove对象。在我们的两个数据触发器中,我们绑定了PlayerName属性。如果值为X,我们就设置前景色为红色;如果为O,就设置为绿色。
当你使用数据触发器的时候要小心。在我们的示例中,我们获得Button类型的样式和名为CellTextStyle的样式作为潜在的选择。这一章我已经写过两次了,每次开始我们都把数据触发器放在按钮样式而不是数据模板的样式中。数据触发器是基于内容的,因此确定你把它们放入了你的内容样式中,而不是你的控件样式。
既然我们已经转移到数据模板——在图5-5中用编程方式设置了样式,我们还没有为每个设置颜色,但是数据触发器给我们带来了之前的那个特征,伴随着我们创建的所有其他样式,正如图5-12所示。
不同于属性触发器——以来于依赖属性通知的改变,数据触发器以来于标准的数据改变通知模式的实现。这些内嵌到.NET的模式将在第4张讨论,例如InotifyPropertyChanged。由于每一个PlayerMove对象都是常量,我们不必实现这个模式,但是你还要使用数据触发器,偶而你需要在你的自定义内容类中实现它。
数据触发器另一种便利的特征是,不必显示地检查null的内容。如果内容为空,这个触发器条件会自动为false,这是为什么应用程序试着从一个空的PlayerMove中获取PalyerName属性,却没有崩溃的原因。
图5-12
5.6.5多重条件数据触发器
正如属性触发器使用MultiTrigger元素编译到“and”条件中,数据触发器也可以这么编译,使用MultiDataTrigger元素。例如,如果我们想要监视游戏的第10次移动,确保是玩家O,以及做一些特殊的事情,正如示例5-29。
示例5-29
示例5-29唯一看上去有点奇怪的是,使用了映射语法从.NET编译集中引进了System命名空间。我们这么做是为了可以把10作为一个整型而不是字符型;否则,多重条件数据触发器不会正确匹配我们的MoveNumber属性。示例5-29的多重条件数据触发器将MoveNumber的背景色设置为黄色,意味着一个根据——祝贺这次特殊的移动,而正常的TTT并没有,但是你可以使用多重条件数据触发器为你自己的种类。
5.6.6事件触发器
属性触发器监视依赖属性的值,数据触发器监视CLR属性的值,而事件触发器监视事件。当一个事件发生,例如Click事件,事件触发器响应激发一个相关的动画行为。动画相当具有挑战性,所以要在第8章介绍。示例5-30阐明了一个简单的动画,当一个空单元格被点击时,这个格子在5秒内从深黄色转换到白色。
示例5-30
添加一个动画到一个样式需要两件事情。首先是一个StoryBoard,带有命名的时间线来描述你想要发生的。在我们的情形中,我们的动画是,按钮的背景笔刷颜色在5秒内从黄色转换到白色。
对于任何带有嵌入式路径的动画的属性,需要有一个显示的属性设置,创建顶级的嵌套。示例5-30中,这意味着我们需要一个Setter元素为Background属性。如果没有创建顶级的嵌套,在运行期就不会有任何动画效果。
第二件需要做的是事件触发器开始了时间线。在我们这种情形,当用户点击应用了CellButtonStyle样式的按钮,如Button的Background,我们开始了这个动作——在StoryBoard中由命名的时间线描述。
写到这里,如果你有一个事件触发器和一个多条件属性触发器,具有相同的原型动画,如Button的Background,确保你把多条件触发器放在了xaml文件中的事件触发器前面;否则,你会得到一个荒谬的运行期错误。
这个动画的结果是,显示了黄色的各种阴影,经过点击的效果如图5-13所示。
图5-13
属性触发器和数据触发器使你设置属性——当属性改变的时候。事件触发器使你触发事件——当事件发生的时候。这两套触发器是相当不同的,例如,你不能使用事件触发器设置一个属性;或者使用属性触发器和数据触发器激活一个事件。所有这些触发器使你增加了一定程度的交互性到应用程序——以一种极好的声明式的方式,具有极少代码或没有代码。