您现在的位置是:网站首页> 编程资料编程资料
WPF路由事件中的三种策略介绍_实用技巧_
2023-05-24
315人已围观
简介 WPF路由事件中的三种策略介绍_实用技巧_
什么是路由事件
路由事件是具有更强传播能力的事件,它可以在元素树中向上冒泡和向下隧道传播,并且能够沿着传播路径被事件处理程序来处理。
路由事件允许事件在某个元素上被处理,即使这个事件源自于另外一个元素。事件路由允许某个元素的事件由另外一个元素引发。
路由事件是一种可以针对元素树中的多个侦听器而不是仅仅针对引发该事件的对象调用处理程序的事件。路由事件是一个CLR事件。
路由事件与一般事件的区别在于:路由事件是一种用于元素树的事件,当路由事件触发后,它可以向上或向下遍历可视树和逻辑树,他用一种简单而持久的方式在每个元素上触发,而不需要任何定制的代码(如果用传统的方式实现一个操作,执行整个事件的调用则需要执行代码将事件串联起来)。
路由事件的路由策略:
所谓的路由策略就是指:路由事件实现遍历元素的方式。
路由事件一般使用以下三种路由策略:
- 冒泡路由事件:冒泡路由事件在包含层次中向上传递,即由事件源向上传递一直到根元素。
- 直接路由事件:直接路由事件与普通的.NET事件是非常相似的,他们都起源于一个元素,并且不能够传递给其它的元素。 只有事件源才有机会响应事件。
- 隧道路由事件:从元素树的根部调用事件处理程序并依次向下深入直到事件源。一般情况下,WPF提供的输入事件都是以隧道/冒泡对实现的。隧道事件常常被称为Preview事件。
1、冒泡路由事件
XAML代码如下:
运行效果如下所示:

当单击Left按钮的时候,Button.Click事件被触发,并且沿着ButtonLeft→CanvasLeft→GridA→GridRoot→Window这条路线向上传递,当单击Right按钮就会沿着ButtonRight→CanvasRight→GridA→GridRoot→Window这条路线向上传递,这里还没有添加监听器,所以是没有反应的。
如何加入监听器,我们可以再XAML中添加,XAML代码如下:
我们在XAML代码中添加了Button.Click="Button_Click"这个事件处理器,就是监听器,并且事件处理交由Button_Click负责,后台Button_Click代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace WpfRouteEventByBubble { /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show("我到达了:" + (sender as FrameworkElement).Name); } } }我们分析一下,那两个参数到底是什么呢?
- 参数一:sender,这是听者,就是监听的地方,如果点击了Left按钮,那么Left按钮就会大声说:“我被点击了”这个事件向上传递,知道到了设有监听Button.Click事件的地方,这个地方就是sender。
- 参数二:是RoutEventArgs类型的,这个参数携带了一些重要信息,例如事件是从哪里来的,上一个传到哪里等,都可以利用这个参数来查询。
运行效果如下:




我们会发现,当点击button按钮时,ButtonLeft、CanvasLeft、GridA、GridRoot中的事件都会触发,这就是冒泡路由策略的功能所在,事件首先在源元素上触发,然后从每一个元素向上沿着树传递,直到到达根元素为止(或者直到处理程序把事件标记为已处理为止),从而调用这些元素中的路由事件。
如果把Button_Click事件修改为:
private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show("我到达了:" + (sender as FrameworkElement).Name); e.Handled = true;//让事件停止冒泡 }则以上事件就不会沿着ButtonLeft→CanvasLeft→GridA→GridRoot→Window这条路线传递下去,只会执行ButtonLeft的事件。
MouseUp就是一个冒泡路由事件,看下面的XAML代码:
Handle first event
当我们点击Image的时候,发发生冒泡路由事件,会一层层的向外传递,传递顺序:Image->StackPanel->Label->Grid->Window。
后端代码如下:
using System.Windows; namespace 路由事件 { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } protected int eventCounter = 0; private void SomethingClicked(object sender, RoutedEventArgs e) { eventCounter++; string message = $"#{eventCounter.ToString()}:\r\n" + $"Sender:{sender.ToString()}\r\n" + $"Source:{e.Source}\r\n" + $"Original Source:{e.OriginalSource}"; lstMessage.Items.Add(message); // Handled允许终止事件的冒泡或者终止隧道过程 // 设置Handled=True,事件就不会继续传递了 e.Handled = (bool)chkHandle.IsChecked; } private void cmdClear_click(object sender, RoutedEventArgs e) { eventCounter = 0; lstMessage.Items.Clear(); } } }运行程序,输出结果如下:

可以看到:输出结果就是按照我们上面的顺序输出的。我们把Grid的MouseUp事件去掉,在看输出结果:

这时就没有Grid控件了。勾选下面的复选框,在执行结果:

这时只有Image控件被触发了,其它控件的MouseUp被终止了。
2、隧道路由事件
隧道路由事件跟冒泡路由事件一样,都是在包含层次中,但是隧道路由事件的传递方向跟冒泡路由事件正好相反:隧道路由事件首先是从根元素上被触发,然后从每一个元素向下沿着树传递,直到到达根元素为止(或者直到到达处理程序把事件标记为已处理为止)。
冒泡路由事件是向上传递,隧道路由事件是向下传递。
隧道路由事件在事件到达恰当的控件之前,为预览事件提供了机会。首先是在窗口级别上,也就是顶级,然后是更具体的容器,最后直到到达按下键时具有焦点的元素。我们很容易就可以识别隧道事件,因为他们都是以单词Preview开头的。WPF通常可以成对的定义冒泡路由事件和隧道路由事件。如果我们找到一个MouseUp的冒泡路由事件,还可以找到一个PreviewMouseUp的隧道路由事件。隧道路由事件总是在冒泡路由事件之前被触发。
XAML代码如下;
后台代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace Wpf路由事件管道策略 { /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } private void Window_PreviewMouseDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("windows被点击"); } private void grid_PreviewMouseDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("grid被点击"); } private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("button被点击"); } } }程序运行效果:
提示:
本文由神整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!
