您的位置:首页 > 其它

[笔记/简译]WPF的新特性——路由事件(1)

2008-03-03 18:17 246 查看
路由事件(Routed Event

就像WPF在.NET属性之上添加了许多基础结构一样,它也在.NET事件之上添加了许多基础结构。路由事件是一种与树上元素协同工作的事件,当它被触发后,能够沿着逻辑树和视觉树上/下传递,触发每个子元素的对应事件,不需要任何自定义的代码。

事件路由帮助大多数应用程序屏蔽视觉树的细节,是WPF中“元素组合”得以成功的关键。例如,Button公开了处理低级MouseLeftButtonDown和KeyDown的Click事件。当用户在标准按钮上单击了鼠标左键,实际上他们是与ButtonChrome或TextBlock视觉子元素进行交互的。由于事件沿着视觉树向上传递,所以Button最终收到这个事件并且能够处理它。相似地,对于之前(在XAML揭秘中)录像机风格的Stop按钮,用户可能直接是在Rectangle逻辑子元素上单击了鼠标左键。由于事件沿着逻辑树向上传递,Button也将会收到这个事件并且可以处理它。

因此,我们可以在像Button这样的元素内嵌入任意复杂的内容,或者赋予它任意复杂的视觉树。尽管如此,在任意内部元素上单击鼠标左键的行为都将导致Click事件被父元素Button触发。试想如果没有路由事件,那么实现这样的功能将不得不编写不少代码。

路由事件的行为和实现与依赖属性很相似,让我们先从路由事件的实现开始。

路由事件的实现

大多数情况下,路由事件与普通的.NET事件看起来非常相似。与依赖属性相同,没有.NET语言(除了XAML)知道路由事件的存在,对其的支持则完全基于WPF API。

下例展示了Button实现Click路由事件的方法,不过事实上Click事件是由Button的基类ButtonBase实现的,不过这并不影响我们的讨论。

public class Button : ButtonBase
{
// 路由事件
public static readonly RoutedEvent ClickEvent;

static Button()
{
// 注册事件
Button.ClickEvent = EventManager.RegisterRoutedEvent("Click",
RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Button));
// ...
}

// 事件包装器(可选)
public event RoutedEventHandler Click
{
add { AddHandler(Button.ClickEvent, value); }
remove { RemoveHandler(Button.ClickEvent, value); }
}

protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
// ...
// 触发事件
RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
// ...
}
}

路由事件的实现与依赖属性的实现十分相似。它在实现类型中是一个RoutedEvent类型的对象,习惯上被public static readonly修饰,且拥有Event后缀。EventManager的RegisterRoutedEvent方法用来向事件系统进行注册新的路由事件,并且仅在实现类型中可选的“事件包装器”(如Click事件)中调用AddHandler和RemoveHandler方法来添加和移除它。这两个方法不是从DependencyObject继承而来的,而是来自System.Windows.UIElement(Button等的高层基类),负责在适当的路由事件身上附加或移除事件处理器(委托)。在OnMouseLeftButtonDown内部,UIElement定义的RaiseEvent方法被调用,传入参数分别为对应的路由事件对象和事件源的引用。

路由策略和事件处理器

注册的时候,每个路由事件都会选择一种路由策略。路由策略是事件触发时沿元素树传递的方式,WPF的事件系统一共包含三种事件路由策略:

(1)下降式(tunneling):事件首先在根元素上被触发,继而沿着元素树向下触发子元素的相应事件,直到到达事件源子元素(或直到事件处理方法将这个事件标记为已处理)为止。

(2)上升式(bubbling):事件首先在事件源元素上被触发,继而沿着元素树向上触发父元素的相应事件,直到达到根元素(或直到事件处理方法将这个事件标记为已处理)为止。

(3)直接式(direct):事件只能在事件源元素上被触发。这与普通的.NET事件类似,但它可以参与路由事件的特殊机制,如“事件触发器”(event trigger)。

路由事件的处理方法拥有和一般.NET事件类似的签名。第一个参数是System.Object类型的事件源对象sender,它保存着附加了该处理函数的元素对象的引用;第二个参数是从RoutedEventArgs(派生自System.EventArgs)派生的类的实例e。RoutedEventArgs公开了四个有用的属性:

(1)Source:位于逻辑树上的事件源元素(如Button)。

(2)OriginalSource:位于视觉树上的事件源元素(如Button的ButtonChrome或TextBlock)。

(3)Handled:一个布尔变量,设置为true表示事件已被处理,它可以精确地中止上升式和下降式路由事件的传递。

(4)RoutedEvent:真正的路由事件对象(如Button.ClickEvent ),它对鉴别相同处理函数处理不同路由事件很有帮助。

Source和OriginalSource的区别仅在于处理物理事件上,如鼠标事件等。对于那些与视觉树上元素不需要有直接关系的事件,Source和OriginalSource引用的是相同的对象,比如Click事件(因为用户可以通过键盘触发它,如在拥有焦点的按钮上按下空格键)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: