一、定义、注册和封装路由事件
与依赖属性一样,路由事件由只读的静态字段表示,在静态构造函数中注册,并通过标准的.NET事件进行封装。
例如,WPF的Button类提供了大家熟悉的Click事件,该事件继承自抽象的ButtonBase基类。下面通过代码说明该事件是如何被定义、注册和封装的:
public abstract class ButtonBase : ContentControl, ICommandSource
{
// 事件定义
public static readonly RoutedEvent ClickEvent;
// 事件注册
static ButtonBase()
{
ClickEvent = EventManager.RegisterRoutedEvent("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ButtonBase));
.......
}
// 事件包装
public event RoutedEventHandler Click
{
add
{
base.AddHandler(ClickEvent, value);
}
remove
{
base.RemoveHandler(ClickEvent, value);
}
}
.......
}
依赖属性是使用DependencyProperty.Register()方法注册的,而路由事件是使用EventManager.RegisterRoutedEvent()方法注册的。
当注册路由事件时,需要指定(1)事件的名称(2)路由类型(3)定义事件处理程序语法的委托(4)拥有事件的类。
路由类型是个RoutingStrategy枚举值:(1)Direct直接路由事件(2)Bubble冒泡路由事件(3)Tunnel隧道路由事件。
通常,路由事件通过普通的.NET事件进行封装,从而使所有.NET语言都能访问它们。事件封装器可使用AddHandler()和RemoveHandler()方法添加和删除已注册的调用程序,这两个方法都是在FrameworkElement基类中定义,并被每个WPF元素继承。
二、引发路由事件
路由事件不是通过传统的.NET事件封装器引发的,而是使用RaiseEvent()方法引发事件,所有元素都从UIElement类继承了该方法。
protected virtual void OnClick()
{
RoutedEventArgs e = new RoutedEventArgs(ClickEvent, this);
base.RaiseEvent(e);// 通过RaiseEvent方法触发路由事件
CommandHelpers.ExecuteCommandSource(this);
}
三、关联事件处理程序
可以使用多种方法关联事件处理程序,但常用的有两种:
(1)在XAML标记中添加事件特性
<TextBlock Margin="3" MouseUp="SomethingClick" Name="tbxTest">
text label
</TextBlock>
// 后台cs代码
private void SomethingClick(object sender, MouseButtonEventArgs e)
{
}
(2)使用代码连接事件
tbxTest.MouseUp += new MouseButtonEventHandler(SomethingClick);
上面的代码创建了一个针对该事件具有正确签名的委托对象,并将该委托指向SomethingClick()方法。然后将该委托添加到tbxTest事件的已注册的事件处理程序列表中。
C#还允许使用更简洁的语法,隐式的创建合适的委托对象:
tbxTest.MouseUp += SomethingClick;
上面的代码方法依赖于事件封装器,事件封装器调用UIElement.AddHandler()方法。
四、共享路由事件
与依赖属性一样,可在类之间共享路由事件的定义。例如,UIElement和ContentElement这两个基类都使用了MouseUp事件。MouseUp事件是否System.Windows.Input.Mouse类定义的。UIElement和ContentElement类只能通过RoutedEvent.AddOwner()方法重用MouseUp事件:
UIElement.MouseUpEvent = Mouse.MouseUpEvent.AddOwner(typeof(UIElement));