路由事件通过EventManager,RegisterRoutedEvent方法注册,通过AddHandler和RemoveHandler来关联和解除关联的事件处理函数;通过RaiseEvent方法来触发事件;通过传统的CLR事件来封装后供用户使用。
如何实现自定义路由事件,可以参考MSDN官网上的文档:如何:创建自定义路由事件
下面的这个demo参考自<葵花宝典--WPF自学手册>。
1、MainWindow.xaml
1 <Window x:Class="WpfApplication1.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:sys="clr-namespace:System;assembly=mscorlib" 5 xmlns:local="clr-namespace:WpfApplication1" 6 Title="MainWindow" Height="518" Width="525" 7 local:MySimpleButton.CustomClick="InsertList" 8 Loaded="Window_Loaded"> 9 <Grid Name="grid1" local:MySimpleButton.CustomClick="InsertList"> 10 <Grid.RowDefinitions> 11 <RowDefinition Height="Auto"></RowDefinition> 12 <RowDefinition Height="*"></RowDefinition> 13 <RowDefinition Height="Auto"></RowDefinition> 14 <RowDefinition Height="Auto"></RowDefinition> 15 16 </Grid.RowDefinitions> 17 <local:MySimpleButton x:Name="simpleBtn" CustomClick="InsertList" > 18 MySimpleBtn 19 </local:MySimpleButton> 20 <ListBox Name="lstMsg" Grid.Row="1"></ListBox> 21 <CheckBox Grid.Row="2" Name="chkHandle">Handle first event</CheckBox> 22 <Button Grid.Row="3" Click="cmdClear_Click">Clear list</Button> 23 </Grid> 24 25 </Window>
在xaml文件中,完成页面的元素布局之后,给几个元素添加了事件处理函数。
(1)给Window添加了Loaded事件的处理函数,还添加了MySimpleButton的CustomClick事件的类事件处理函数
1 local:MySimpleButton.CustomClick="InsertList" 2 Loaded="Window_Loaded"
(2)给Grid同样添加了MySimpleButton的类事件处理函数
(3)给MySimpleButton元素添加了CustomClick事件的实例事件处理函数
CustomClick="InsertList"
(4)给Button元素添加了Click事件处理函数
Click="cmdClear_Click"
2、MySimpleButton.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows.Controls; 7 using System.Windows; 8 9 namespace WpfApplication1 10 { 11 //继承Button类,自定义一个名为MySimpleButton的Button 12 public class MySimpleButton:Button 13 { 14 //———————————类事件处理函数———————————— 15 static MySimpleButton() 16 { 17 //为路由事件CustomClickEvent注册一个类事件处理函数 18 //类事件处理函数的优先权高于实例事件处理函数 19 EventManager.RegisterClassHandler(typeof(MySimpleButton), CustomClickEvent, new RoutedEventHandler(CustomClickClassHandler), false); 20 } 21 //创建一个名为CustomClickClassHandler的类事件处理函数 22 //为了通知外部窗口,把路由事件的信息输出,需要添加一个普通的CLR事件ClassHandlerProcessed 23 public event EventHandler ClassHandlerProcessed; 24 public static void CustomClickClassHandler(object sender, RoutedEventArgs e) 25 { 26 MySimpleButton simpleBtn = sender as MySimpleButton; 27 EventArgs args = new EventArgs(); 28 simpleBtn.ClassHandlerProcessed(simpleBtn, args); 29 } 30 31 //———————————实例事件处理函数———————————— 32 //创建和注册一个名为CustomClickEvent的路由事件,路由策略为Bubble 33 public static readonly RoutedEvent CustomClickEvent = EventManager.RegisterRoutedEvent("CustomClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MySimpleButton)); 34 //给路由事件添加一个CLR事件包装器 35 public event RoutedEventHandler CustomClick 36 { 37 add 38 { 39 AddHandler(CustomClickEvent, value); 40 } 41 remove 42 { 43 RemoveHandler(CustomClickEvent, value); 44 } 45 } 46 //RaiseEvent()触发CustomClickEvent事件 47 protected override void OnClick() 48 { 49 RaiseCustomClickEvent(); 50 } 51 void RaiseCustomClickEvent() 52 { 53 RoutedEventArgs newEventArgs = new RoutedEventArgs(MySimpleButton.CustomClickEvent); 54 RaiseEvent(newEventArgs); 55 } 56 57 } 58 }
这个是自定义的一个按钮类,在里面创建了自定义的路由事件。
3、MainWindow.xaml.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Windows; 7 using System.Windows.Controls; 8 using System.Windows.Data; 9 using System.Windows.Documents; 10 using System.Windows.Input; 11 using System.Windows.Media; 12 using System.Windows.Media.Imaging; 13 using System.Windows.Navigation; 14 using System.Windows.Shapes; 15 16 namespace WpfApplication1 17 { 18 /// <summary> 19 /// Interaction logic for MainWindow.xaml 20 /// </summary> 21 public partial class MainWindow : Window 22 { 23 public MainWindow() 24 { 25 InitializeComponent(); 26 //MySimpleButton的类事件处理函数处理过,window就能得到通知 27 this.simpleBtn.ClassHandlerProcessed += new EventHandler(simpleBtn_RaisedClass); 28 } 29 protected int eventCount = 0; 30 //CusomClick的事件处理函数 31 private void InsertList(object sender, RoutedEventArgs e) 32 { 33 eventCount++; 34 string msg = "#" + eventCount.ToString() + ":\r\n" + "InsertList\r\n" + "Sender:" + sender.ToString() + "\r\n Source:" + e.Source+"\r\n"+"Original Source:"+e.OriginalSource; 35 lstMsg.Items.Add(msg); 36 //CheckBox选中状态表示路由事件是否已处理,若已处理,则不在传递 37 e.Handled = (bool)chkHandle.IsChecked; 38 } 39 //类事件处理函数已经完成,打印信息 40 private void simpleBtn_RaisedClass(object sender, EventArgs e) 41 { 42 eventCount++; 43 string msg = "#" + eventCount.ToString() + ":\r\n WindowClassHandler\r\nSender:" + sender.ToString(); 44 lstMsg.Items.Add(msg); 45 } 46 //Clear列表内容 47 private void cmdClear_Click(object sender, RoutedEventArgs e) 48 { 49 eventCount = 0; 50 lstMsg.Items.Clear(); 51 } 52 //在window的Load事件中给Grid另外添加一个名为ProcessHandlersToo的路由事件处理函数 53 //通过这种方式添加,即使路由事件被标记"已处理",处理函数仍然会执行 54 private void Window_Loaded(object sender, RoutedEventArgs e) 55 { 56 grid1.AddHandler(MySimpleButton.CustomClickEvent, new RoutedEventHandler(ProcessHandlerToo), true); 57 } 58 59 private void ProcessHandlerToo(object sender, RoutedEventArgs e) 60 { 61 eventCount++; 62 string msg = "#" + eventCount.ToString() + ":\r\n" + "InsertList\r\n" + "Sender:" + sender.ToString() + "\r\n Source:" + e.Source + "\r\n" + "Original Source:" + e.OriginalSource; 63 lstMsg.Items.Add(msg); 64 65 } 66 } 67 }
上面是路由事件的具体处理。
4、运行效果
从上面的运行效果可以看到,
(1)CheckBox未选中
路由事件在传递时,首先被类事件处理函数处理,然后沿着视觉树向上传递(MySimpleButton-->Grid-->Window),依次被添加了实例事件处理函数的元素进行事件处理。在传到Grid元素时,先进行InserList处理,再进行ProcessHandlerToo处理,这两个事件处理函数是用不同方式添加的,执行顺序不同。
(2)CheckBox选中
选中了CheckBox,则路由事件传递到MySimpleButton元素并进行处理后,被标记成"已处理",则之后不再向上传递,Grid和Window元素不再执行InsertList,但是Grid中的处理函数ProcessHandlerToo仍然会执行,这是两种事件添加方式不同的地方。