解读WPF中事件
WPF中的事件我们称之为路由事件。那么它和.NET的传统事件有什么区别了。我们首先来了解下路由事件。
路由事件是可以在UI树形结构进行传递的。也就是说激发控件可以把这个事件消息传递到根节点上去。在树形节点
上,只要监听器,都可以读到这个事件信息。这样可以看出,路由事件是一对多的。而传统的.NET事件却是单对单的。
好了,废话不多说。我们先来看看路由事件是如何使用的。路由事件的注册和我们的依赖属性是差不多的。
大致就是以下三个步骤:
/*
* 路由事件的定义
* 1、声明路由事件
* 路由事件是一个静态的,只读的,类型是RoutedEvent
* 使用EventManger来进行注册
*
* 2、定义CLR包装器来进行事件的添加和删除
*
* 3、激发路由事件
*/
我们现在来看几个例子:
第一个例子路由事件的传递性:
XAML:
<Window x:Class="RouteEventWPF.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid x:Name="Grid1" ButtonBase.Click="MyBtn_Click">
<Grid x:Name="Grid2" ButtonBase.Click="MyBtn_Click">
<Grid x:Name="Grid3" ButtonBase.Click="MyBtn_Click">
<Button x:Name="MyBtn" Click="MyBtn_Click" Content="Test"></Button>
</Grid>
</Grid>
</Grid>
</Window>
以上代码大家是不是看到一个ButtonBase.Click这个是说明该Grid监听了鼠标点击事件(下面将给出后台添加监听器)
后台代码:
/// <summary>
/// Window1.xaml 的交互逻辑
/// </summary>
public partial class Window1 : Window{
public Window1()
{
InitializeComponent();
}
private void MyBtn_Click(object sender, RoutedEventArgs e)
{
FrameworkElement element = sender as FrameworkElement;
MessageBox.Show(element.Name);
}
}
当我们点击了按钮会弹出四个框出来。分别是MyBtn,Grid3,Grid2,Grid1大家可以试试
上面说了。这里在XAML文件中添加了监听器了。 现在我们在后台添加监听器:
// 第一个参数表示要监听什么样的事件。第二个参数表示处理程序是什么,这里是用到了委托
this.Grid3.AddHandler(Button.ClickEvent, new RoutedEventHandler(MyBtn_Click));
到目前为止,我们都没有看到一开始所说的事件的注册。好了。现在我们自己定义一个路由事件。来看看
路由事件的整体注册使用流程:
第一步:定义路由事件类 /// <summary> /// 自定义路由事件 /// </summary> public class ReportTimeEventArgs : RoutedEventArgs { /// <summary> /// 调用父类的构造 /// </summary> /// <param name="routedEvent"></param> /// <param name="sender"></param> public ReportTimeEventArgs(RoutedEvent routedEvent,Object sender):base(routedEvent,sender){} /// <summary> /// 定义自己的属性 /// </summary> public DateTime ClickTime { get; set; } } 第二步:在我们要用到的地方定义,我这里重写了Button类。 /// <summary> /// 定义一个自己的Button /// 在里面注册自己定义的路由事件 /// </summary> public class TimeButton : Button { /* * 路由事件的定义 * 1、声明路由事件 * 路由事件是一个静态的,只读的,类型是RoutedEvent * 使用EventManger来进行注册 * * 2、定义CLR包装器来进行事件的添加和删除 * * 3、激发路由事件 */ /// <summary> /// 路由事件的声明 /// </summary> public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent("ReportTime", RoutingStrategy.Tunnel, typeof(EventHandler<ReportTimeEventArgs>), typeof(TimeButton)); /// <summary> /// 事件添加和删除 /// </summary> public event RoutedEventHandler ReportTime { add { this.AddHandler(ReportTimeEvent, value); } remove { this.RemoveHandler(ReportTimeEvent, value); } } /// <summary> /// Button的点击 /// 触发路由事件 /// </summary> protected override void OnClick() { base.OnClick(); ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent,this); args.ClickTime = DateTime.Now; this.RaiseEvent(args); } } 看到了吧这里就是我们定义路由事件的效果了啊。 第三步:使用我们定义的路由 <Window x:Class="RouteEventWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:RouteEventWPF.Until" local:TimeButton.ReportTime="Window_ReportTime" Title="MainWindow" Height="350" Width="525" x:Name="RoutedTitle"> <Grid x:Name="grid_1" local:TimeButton.ReportTime="Window_ReportTime"> <Grid x:Name="grid_2" local:TimeButton.ReportTime="Window_ReportTime"> <Grid x:Name="grid_3" local:TimeButton.ReportTime="Window_ReportTime"> <StackPanel x:Name="stackPanel_1" local:TimeButton.ReportTime="Window_ReportTime" > <ListBox x:Name="lstBox" Height="230"></ListBox> <local:TimeButton x:Name="timeButton" Width="80" Height="80" Content="报时" local:TimeButton.ReportTime="Window_ReportTime"/> </StackPanel> </Grid> </Grid> </Grid> </Window> 大家要注意这里几个地方: xmlns:local="clr-namespace:RouteEventWPF.Until"这是应用我们刚才定义的几个类的命名空间 后台代码很简单: 请看代码如下: private void Window_ReportTime(object sender, ReportTimeEventArgs e) { FrameworkElement element = sender as FrameworkElement; string str = e.ClickTime.ToLongTimeString(); string content = string.Format("{0}到达{1}", str, element.Name); this.lstBox.Items.Add(content); // 让路由事件停止,不再向上传递 if (element.Name.Equals("grid_2")) { e.Handled = true; } } 这段代码有部分要注意,如何让路由事件到了某个节点就不再继续传递了。这里给出了例子: e.Handled = true;把这个设置成True就可以了。注意了哦啊。 以上就是路由事件的注册和使用方法。大家去敲一敲。看看效果。
在这里再给大家补充一个东西就是附加路由。意思就是把一个路由事件附加到别的对象上。
这里我们来看看:
我定义了一个Student类
public class Student { /// <summary> /// 注册路由 /// </summary> public static readonly RoutedEvent StudentEventArgs = EventManager.RegisterRoutedEvent("StudentEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Student)); public string ID { get; set; } public string Name { get; set; } /* * 够着CLR包装器 */ /// <summary> /// 添加路由事件 /// </summary> /// <param name="d">依赖属性,给哪一个UI添加</param> /// <param name="h">路由委托</param> public static void AddNameChangedHandler(DependencyObject d, RoutedEventHandler h) { UIElement e = d as UIElement; if (e != null) { e.AddHandler(Student.StudentEventArgs, h); } } /// <summary> /// 删除 /// </summary> /// <param name="d"></param> /// <param name="h"></param> public static void RemoveNameChangedHandler(DependencyObject d,RoutedEventHandler h) { UIElement e = d as UIElement; if (e != null) { e.RemoveHandler(Student.StudentEventArgs, h); } } } 我们来使用这个所谓的附加路由: <Window x:Class="RouteEventWPF.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:RouteEventWPF.Until" Title="MainWindow" Height="350" Width="525" x:Name="RoutedTitle"> <Grid x:Name="grid_1"> <Button Content="注册事件" x:Name="btnAttachEvent" Height="80" Click="btnAttachEvent_Click"></Button> </Grid> </Window> 后台代码 public MainWindow() { InitializeComponent(); // this.grid_1.AddHandler(Student.StudentEventArgs, new RoutedEventHandler(Student_Hander)); Student.AddNameChangedHandler(this.grid_1, new RoutedEventHandler(Student_Hander)); } private void Student_Hander(object sender,RoutedEventArgs e) { MessageBox.Show((e.OriginalSource as Student).Name.ToString()); } private void btnAttachEvent_Click(object sender, RoutedEventArgs e) { Student stu = new Student() { ID = "10001", Name = "WangYu" }; stu.Name = "Tom"; RoutedEventArgs args = new RoutedEventArgs(Student.StudentEventArgs, stu); this.btnAttachEvent.RaiseEvent(args); }
大家不知道有没有发现。这里的是通过Button把这个路由事件给抛出去的。而不是自己抛出去的。 所以称这样的事件为附加路由。 为什么他不能自己抛出去了啊。主要是因为我定义的这个类没有RaiseEvent方法。所以他没有办法抛出去。
关于路由事件的具体就是这么多。 哦,漏和大家说了一定东西。就是路由传递形式。 路由的传递形式有三种。大家在注册路由的时候有没有发现这个参数 RoutingStrategy.Bubble 这里就说明了路由的传递方式。 RoutingStrategy这个枚举有三个。分别是Tunnel,Bubble,Direct 这三个什么意思。这里我不想说。大家试着去改改上面的代码,自己跑出看看。
好了。各位今天的WPF路由事件就到这里。电话都给朋友打爆了。再不出门就要出事了。拜拜。周末最后一个下午。 大家好好玩哦啊。