wpf中的触发器详解

原文 http://zwkufo.blog.163.com/blog/static/25882512009724113250883/

7.1.2  简单逻辑的表示--触发器(1)

在本章的多处介绍中都会涉及触发器的使用。顾名思义,触发器(Trigger)就是当某种条件满足后即完成相应逻辑功能的一部分程序组成。在当前的WPF中,Trigger一共有三种类型,它们分别是:

(1)属性触发器:其对应的类是Trigger。它在特定关联属性发生变化时被触发。

(2)数据触发器:其对应的类是DataTrigger。它在特定的CLR类型所记录的值发生变化时被触发。

(3)事件触发器:其对应的类是EventTrigger。它将在特定的路由事件发生时被触发。

在WPF中,每一个可以使用触发器的类中都会有一个Triggers属性。拥有这个属性的类有:FrameworkElement,Style,DataTemplate和ControlTemplate。但是需要注意的是,FrameworkElement类只支持EventTrigger。这是因为微软还没有完成它对其他两类触发器的支持。如果程序中需要使用属性触发器或数据触发器的功能,软件开发人员就需要使用设置样式触发器的方法对触发器进行一次包装,再将该样式应用在FrameworkElement类的实例上。因此就现在来说,Trigger和EventTrigger仅可以用在控件模板或样式中,而DataTrigger则只能用在数据模板之中。本书对样式以及模板的介绍将在本章的稍后部分进行。因此在这里所讲解的对触发器的应用仅限于对Trigger元素的直接使用,而并不涉及其他复杂的使用。

同时,为了支持对复杂触发条件的表示,WPF还引入了MultiTrigger和MultiDataTrigger完成对与逻辑的支持。如果想用触发器表示或逻辑,软件开发人员可以通过将多个触发器同时放置到Triggers属性中完成。

1.触发器使用准则

不论是上面的哪种触发器,都不能脱离WPF对用户界面进行定义的三个准则。而这三个准则不仅导致触发器成为了WPF的一部分,更重要的是,还完成了对触发器使用规范的定义。

(1)元素合成

经过对前面几章知识的学习,相信读者已经习惯了使用多个界面元素定义一个具有特定功能的控件。例如在3.2节中,就通过将一个图像放置在按钮控件中完成了对位图按钮的定义。由于Image类是FrameworkElement类的派生类,可以接受用户输入,因此当鼠标位于Image上方并按下鼠标左键的时候,鼠标左键被按下的消息不仅由Button类实例接收到,更重要的是,Image类实例也接收到这个消息,并且是由该实例将这个消息,传送到Button类实例中的。这个由元素合成所导致的现象也将触发源定义的灵活性引入到了触发器的使用中。即对于此例,软件开发人员不仅可以将触发器的触发源定义为Image类实例,更可以定义为Button类实例。将触发源定义为Button类实例的好处是:软件在处理Button类实例的鼠标左键消息的同时也就处理了Image类实例的鼠标左键消息。

元素合成对触发器使用的影响不仅如此。实际上WPF中的各个控件都是由其他界面元素组成的,比如组成按钮控件的Border,ButtonChrome等。那么在使用XAML定义一个控件的外观,也就是该控件的ControlTemplate的时候,软件开发人员就需要考虑触发器消息源的位置。

(2)界面与行为分离

或许用户还没有体会到,WPF的另一个特点就是让用户界面定义与软件的业务逻辑分离。也就是说,在程序员编写软件的业务逻辑的时候,用户界面设计师同时可以将软件所需的界面通过XAML描述出来。

假如软件开发人员正在编写一个用户注册系统,该系统要求用户输入自己的用户名并重复输入两次密码完成对该用户名的注册。而且该系统的主要特色就是当用户重复输入密码发生错误的时候,第二个密码输入框的边框将显示为红色。以当前所介绍的知识来看,软件开发人员需要使用一个函数侦听TextBox的TextChanged消息。在两个密码框中的字符并不是包含关系的时候,该消息的处理函数会将第二个消息框的边框设置为红色。也就是说,在了解触发器之前,该功能需要在C#代码中实现。

实际上,这只是一个界面上的功能,而与后台程序的业务逻辑完全没有关系。因此软件开发人员需要在XAML中使用一种方法完成该功能。这个方法就是使用触发器。

(3)选择合适的触发条件

在WPF中,用户可以发现许多貌似重复的事件以及函数。比如IInputElement接口已经实现了表示鼠标左键点击的MouseLeftButtonDown事件,而在ButtonBase类中WPF又为相同行为添加了Click事件。该事件不仅表示点击鼠标左键导致的按钮被按下这一行为,也表示默认按钮在用户按下回车键时被按下或当前具有输入焦点的按钮在用户按下空格键时被按下这一行为。另外一个例子是TextBox类不仅有GotFocus这一事件,更有GotKeyboardFocus,GotStylusCapture和GotMouseCapture等事件。也就是说,Click事件以及GotFocus事件都是具有更强大功能的事件,而且可以预计的是,各种WPF控件中还有许多这样的类似情况。因此在XAML中定义触发器的时候,软件开发人员一定要考虑清楚触发器的实际触发条件。

7.1.2  简单逻辑的表示--触发器(2)

2.触发器相关知识

在系统地介绍各个触发器之前,请读者先来看看与触发器相关的知识。本节所介绍的知识包括触发器基类TriggerBase类的继承结构,以及Setter类的使用。

(1)TriggerBase类的继承结构

首先,本书还是从各个触发器的基类的继承结构开始讲解。图7-1是TriggerBase类的继承结构。

图7-1  TriggerBase类的继承结构

可以看到,TriggerBase类是一个虚基类。该类直接派生自DependencyObject类,并只引入了两个新的属性:EnterActions和ExitActions。这两个属性分别表示所侦听的属性触发当前触发器时以及离开触发状态时所要执行的动作。但是,由于EventTrigger表示发生事件的一个时间点,而并不是保持在某一种状态的一段时间,所以EventTrigger并不支持对该属性的使用。为了赋予EventTrigger相同的功能,WPF为它添加了Actions属性。对该属性的介绍将安排到讲解事件触发器时。为了使读者更清楚地了解EnterActions和ExitActions属性的使用,下面列出了一个使用这两个属性的例子:

代码App7.2

<Trigger Property="IsMouseOver" Value="True">
<Trigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</Trigger.EnterActions>
<Trigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" To="0.25" Duration="0:0:1"/>
</Storyboard>
</BeginStoryboard>
</Trigger.ExitActions>
</Trigger>

在代码App7.2中,Trigger类作为TriggerBase类的直接派生类被使用。Trigger类中新引入了四个属性,按照逻辑关系,它们是SourceName,Property,Value和Setters。当需要定义一个触发器时,软件开发人员需要使用SourceName定义触发器事件的起源。在触发条件与拥有该触发器的界面元素没有关系的情况下,该属性用来对触发器事件的起源进行指向。在确定了触发器事件的起源后,XAML代码就可以设定该触发器需要侦听的属性和需要比较的值。当属性中记录的值与比较值相同时,WPF就需要执行Setters中记录的各个Setter,完成触发器的控制功能。

(2)Setter类的使用

Setter类是非常容易使用的。它具有三个常用的属性:TargetName,Property和Value。这三个属性分别表示需要设置属性所在实例的名称、需要进行设置的属性和需要将该属性设置的值。但需要注意的是,被设置的属性必须是关联属性。当Trigger或Style中设置了TargetType的时候,XAML可以直接指定需要设置的属性而省略对象的类型。但在没有指定TargetType的情况下,Setter中对TargetType类型的Property属性的设置就必须使用TargetType.Property的形式。例如,当需要使用Setter元素设置按钮控件的背景颜色为蓝色时,软件开发人员就可以使用下面的XAML语句:

<Setter Property="Button.Background" Value="Blue"/>
从MSDN对Setter类的基类SetterBase的介绍中可以看到,Setter类的基类SetterBase不只有一个派生类。除了Setter类之外,SetterBase类的派生类还有一个EventSetter类。EventSetter类用来完成对事件处理函数的定义。例如,若想让一个Button类实例在鼠标移动到其上时运行OnMouseEnter函数,软件开发人员就可以使用下面的代码:
<EventSetter Event="Button.MouseEnter" Handler="OnMouseEnter"/>

但是从WPF将用户界面定义与逻辑代码分离的设计思想来看,EventSetter无疑是一个不太友好的设计。而且在不同地方使用不同的EventSetter的情况下,软件开发人员并没有一个好的办法判断各个事件处理函数被执行的先后次序。而在一个以事件作为驱动的程序中,无法对事件响应函数的执行顺序进行控制无疑是一件最危险的事情。

3.属性触发器

首先来看看属性触发器。属性触发器在指定的属性具有指定的值时,执行它所包含的一系列Setter完成对其他属性的设置。而当该属性不再是该指定值时,所有的属性设置将被恢复到前一状态。

代码App7.3就是一个使用触发器的例子。

代码App7.3

<TextBox TextWrapping="Wrap" Margin="5">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="text">
<Setter Property="Background" Value="Aqua"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

在包含上例代码的程序中,如果用户在文本输入框中输入"text",输入框的背景颜色将变成绿色。完成这种控制逻辑的就是在Style中定义的属性触发器Trigger。在Trigger的声明中,对Trigger各属性的设置声明了Trigger被触发的条件:当Text属性为字符串"text"的时候,执行Setter中对属性的设置,即将背景颜色变成绿色。

4.数据触发器

除了Trigger类可以用来侦听属性的变化外,软件开发人员还可以使用DataTrigger完成对任意类型的CLR数据变化的侦听。因此,DataTrigger类不仅可以完成Trigger类的所有功能,更可以运行非关联属性的更改触发逻辑。DataTrigger类一共引入了三个参数:Binding,Value和Setters。当需要设置数据触发器侦听的数据源时,软件开发人员应该以通过绑定对Binding属性赋值的方式来完成。即如果需要使用DataTrigger完成上面对TextBox背景颜色进行更改的功能,软件开发人员就需要使用示例代码App7.4。

代码App7.4

<TextBox TextWrapping="Wrap" Margin="5">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="text">
<Setter Property="Background" Value="Aqua"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

7.1.2  简单逻辑的表示--触发器(3)

需要注意的是:虽然软件开发人员可以使用DataTrigger完成对任意CLR类型数据变化的侦听,但Setter只能对关联属性进行设置。并且XAML不能在Setter中对Style属性进行更改。其原因是:触发器可以在样式中进行定义。当一个在样式中定义的触发器更改了其所在实例的样式时,WPF怎么继续处理触发器中剩余的设置?为了避免这个问题,WPF禁止在触发器中对样式进行设置。

5.事件触发器

WPF中还提供另一种触发器。该触发器的触发条件就是一个事件的发生。该触发器所对应的类为EventTrigger,即事件触发器。该类从TriggerBase类派生后只添加了三个属性:Actions,RoutedEvent和SourceName。软件开发人员可以通过SourceName属性指定激活该触发器的元素名称。而RoutedEvent属性则记录激活该触发器的事件。Actions是一个只读属性,表示触发器被触发时需要执行的动作。该类型的触发器将在第10章中进行讲解。

6.或逻辑触发器

当需要表示或逻辑关系时,软件开发人员可以简单地将多个触发器并列。当某一个触发器所标识的条件满足时,该触发器所包含的行为将执行,导致使用这个触发器的用户界面元素实例的属性改变。如果在前面的例子中,软件设计师不仅希望TextBox 的背景颜色在用户输入为"text"时为绿,也希望背景在用户输入为"text."时为绿,那么对该要求的实现逻辑用XAML语句见代码App7.5。

代码App7.5

<TextBox TextWrapping="Wrap" Margin="5">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}"Value="text">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Text}" Value="text.">
<Setter Property="Background" Value="Blue"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

在或逻辑关系中,触发器的各属性匹配可能在同一时间被满足。在这种情况下,触发器对状态的设置同时生效。在各个触发器对属性的设置发生冲突时,WPF将按照后声明的触发器所制定的规则对属性进行设置。可参考对于代码App7.6中的触发器定义。

代码App7.6

<Button Content="Press Me!">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="Button.IsMouseOver" Value="True">
<Setter Property="Button.Foreground" Value="Blue"/>
</Trigger>
<Trigger Property="Button.IsPressed" Value="True">
<Setter Property="Button.Foreground" Value="Red"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

当用户将鼠标移动到按钮上面并使用左键对按钮进行点击的时候,按钮的颜色将是红色,而不是蓝色。因为在按钮被按下之前,IsMouseOver属性的值为True,而在按钮被按下时IsPressed属性的值也变为True,所以按照后声明优先的决定方式,WPF将设置该按钮的背景颜色为红色。

7.与逻辑触发器

如果要表示与逻辑关系,软件开发人员就需要使用MutiTrigger或MutiDataTrigger。在使用这两种触发器时,软件开发人员需要向它们的Conditions集合中添加触发条件。假设软件需要下面一种功能:当TextBox中所记录的字符串是"text"并且鼠标在TextBox之上时,TextBox的背景颜色将变成绿色。完成该功能的XAML语句如示例代码App7.7所示。

代码App7.7

<TextBox TextWrapping="Wrap" Margin="5">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Text" Value="text"/>
<Condition Property="IsMouseOver" Value="True"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="Aqua"/>
</MultiTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>

【from 《WPF全视角分析》 http://book.51cto.com  2009-06-10 11:57  张晗雨  机械工业出版社】

 

 

时间: 2024-10-30 01:34:49

wpf中的触发器详解的相关文章

Hibernate配置文件中映射元素详解

详解 本文中将讲述Hibernate的基本配置及配置文件的应用,这对于正确熟练使用Hibernate是相当关键的. 配置文件中映射元素详解 对象关系的映射是用一个XML文档来说明的.映射文档可以使用工具来生成,如XDoclet,Middlegen和AndroMDA等.下面从一个映射的例子开始讲解映射元素,映射文件的代码如下. <?xml version="1.0"?><!--所有的XML映射文件都需要定义如下所示的DOCTYPE.Hibernate会先在它的类路径(c

java-在myeclipse中svn使用详解

问题描述 在myeclipse中svn使用详解 在myeclipse中svn使用详解.比如:标记为合并是在什么情况下用, 覆盖更新:在什么情况下使用等等. 本人对svn不是很了解,尤其是在有冲突文件的时候. 说的尽量详细点... 请各位大神指教~~~ 解决方案 如何在MyEclipse下集成SVN详解如何在MyEclipse下集成SVN详解MyEclipse中SVN使用步骤 解决方案二: myeclipse6.5集成svn 一.安装方法: 方法一.如果可以上网可在线安装 打开Myeclipse,

c# 怎样读取mobi文件或mobi格式中文文档详解

问题描述 c#怎样读取mobi文件,mobi格式中文文档详解,谁有源码或文档的,先谢过了 解决方案 解决方案二:没人回答?..解决方案三: 解决方案四:好吧我不知道--不过帮你谷歌了--网上没找到--应该是没这需求吧--mobi我记得用在了Kindle上不过应该是可以读的,因为Windows8.1上有读取这个的App,你可以逆向一下--解决方案五:https://mbc.codeplex.com/这里有demo下载看看

ASP 中 DateDiff 函数详解 主要实现两日期加减操作_ASP基础

ASP 中 DateDiff 函数详解DateDiff 函数  描述  返回两个日期之间的时间间隔.  语法  DateDiff(interval, date1, date2 [,firstdayofweek][, firstweekofyear]])  DateDiff 函数的语法有以下参数:  参数 描述  interval 必选.字符串表达式,表示用于计算 date1 和 date2 之间的时间间隔.有关数值,请参阅"设置"部分.  date1, date2 必选.日期表达式.用

Java中final关键字详解_php技巧

谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. 主要介绍:一.final关键字的基本用法.二.深入理解final关键字 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能

Java 中的注解详解及示例代码_java

在Java中,注解(Annotation)引入始于Java5,用来描述Java代码的元信息,通常情况下注解不会直接影响代码的执行,尽管有些注解可以用来做到影响代码执行. 注解可以做什么 Java中的注解通常扮演以下角色 编译器指令 构建时指令 运行时指令 其中 Java内置了三种编译器指令,本文后面部分会重点介绍 Java注解可以应用在构建时,即当你构建你的项目时.构建过程包括生成源码,编译源码,生成xml文件,打包编译的源码和文件到JAR包等.软件的构建通常使用诸如Apache Ant和Mav

xcode 左边导航栏中符合含义详解_IOS

xcode 左边导航栏中符合含义详解 "M" = Locally modified    "U" = Updated in repository  "A" = Locally added    "D" = Locally deleted    "I" = Ignored  "R" = Replaced in the repository  "–" "=&qu

从汇编看c++中的多态详解_C 语言

在c++中,当一个类含有虚函数的时候,类就具有了多态性.构造函数的一项重要功能就是初始化vptr指针,这是保证多态性的关键步骤. 构造函数初始化vptr指针 下面是c++源码: class X { private: int i; public: X(int ii) { i = ii; } virtual void set(int ii) {//虚函数 i = ii; } }; int main() { X x(1); } 下面是对应的main函数汇编码: _main PROC ; 16 : in

php中urlencode()函数详解说明

php教程中urlencode()函数详解说明 URLEncode:是指针对网页url中的中文字符的一种编码转化方式,最常见的就是Baidu.Google等搜索引擎中输入中文查询时候,生成经过Encode过的网页URL. URLEncode的方式一般有两种,一种是传统的基于GB2312的Encode(Baidu.Yisou等使用),另一种是基于UTF-8的Encode(Google.Yahoo等使用). 本工具分别实现两种方式的Encode与Decode: 中文 -> GB2312的Encode