網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
什么是路由事件
路由事件是具有更強(qiáng)傳播能力的事件,它可以在元素樹中向上冒泡和向下隧道傳播,并且能夠沿著傳播路徑被事件處理程序來處理。
路由事件允許事件在某個(gè)元素上被處理,即使這個(gè)事件源自于另外一個(gè)元素。事件路由允許某個(gè)元素的事件由另外一個(gè)元素引發(fā)。
路由事件是一種可以針對(duì)元素樹中的多個(gè)偵聽器而不是僅僅針對(duì)引發(fā)該事件的對(duì)象調(diào)用處理程序的事件。路由事件是一個(gè)CLR事件。
路由事件與一般事件的區(qū)別在于:路由事件是一種用于元素樹的事件,當(dāng)路由事件觸發(fā)后,它可以向上或向下遍歷可視樹和邏輯樹,他用一種簡(jiǎn)單而持久的方式在每個(gè)元素上觸發(fā),而不需要任何定制的代碼(如果用傳統(tǒng)的方式實(shí)現(xiàn)一個(gè)操作,執(zhí)行整個(gè)事件的調(diào)用則需要執(zhí)行代碼將事件串聯(lián)起來)。
路由事件的路由策略:
所謂的路由策略就是指:路由事件實(shí)現(xiàn)遍歷元素的方式。
路由事件一般使用以下三種路由策略:
- 冒泡路由事件:冒泡路由事件在包含層次中向上傳遞,即由事件源向上傳遞一直到根元素。
- 直接路由事件:直接路由事件與普通的.NET事件是非常相似的,他們都起源于一個(gè)元素,并且不能夠傳遞給其它的元素。 只有事件源才有機(jī)會(huì)響應(yīng)事件。
- 隧道路由事件:從元素樹的根部調(diào)用事件處理程序并依次向下深入直到事件源。一般情況下,WPF提供的輸入事件都是以隧道/冒泡對(duì)實(shí)現(xiàn)的。隧道事件常常被稱為Preview事件。
1、冒泡路由事件
XAML代碼如下:
運(yùn)行效果如下所示:
當(dāng)單擊Left按鈕的時(shí)候,Button.Click事件被觸發(fā),并且沿著ButtonLeft→CanvasLeft→GridA→GridRoot→Window
這條路線向上傳遞,當(dāng)單擊Right按鈕就會(huì)沿著ButtonRight→CanvasRight→GridA→GridRoot→Window
這條路線向上傳遞,這里還沒有添加監(jiān)聽器,所以是沒有反應(yīng)的。
如何加入監(jiān)聽器,我們可以再XAML中添加,XAML代碼如下:
我們?cè)赬AML代碼中添加了Button.Click="Button_Click"這個(gè)事件處理器,就是監(jiān)聽器,并且事件處理交由Button_Click負(fù)責(zé),后臺(tái)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("我到達(dá)了:" + (sender as FrameworkElement).Name); } } }
我們分析一下,那兩個(gè)參數(shù)到底是什么呢?
- 參數(shù)一:sender,這是聽者,就是監(jiān)聽的地方,如果點(diǎn)擊了Left按鈕,那么Left按鈕就會(huì)大聲說:“我被點(diǎn)擊了”這個(gè)事件向上傳遞,知道到了設(shè)有監(jiān)聽Button.Click事件的地方,這個(gè)地方就是sender。
- 參數(shù)二:是RoutEventArgs類型的,這個(gè)參數(shù)攜帶了一些重要信息,例如事件是從哪里來的,上一個(gè)傳到哪里等,都可以利用這個(gè)參數(shù)來查詢。
運(yùn)行效果如下:
我們會(huì)發(fā)現(xiàn),當(dāng)點(diǎn)擊button按鈕時(shí),ButtonLeft、CanvasLeft、GridA、GridRoot中的事件都會(huì)觸發(fā),這就是冒泡路由策略的功能所在,事件首先在源元素上觸發(fā),然后從每一個(gè)元素向上沿著樹傳遞,直到到達(dá)根元素為止(或者直到處理程序把事件標(biāo)記為已處理為止),從而調(diào)用這些元素中的路由事件。
如果把Button_Click事件修改為:
private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show("我到達(dá)了:" + (sender as FrameworkElement).Name); e.Handled = true;//讓事件停止冒泡 }
則以上事件就不會(huì)沿著ButtonLeft→CanvasLeft→GridA→GridRoot→Window
這條路線傳遞下去,只會(huì)執(zhí)行ButtonLeft的事件。
MouseUp就是一個(gè)冒泡路由事件,看下面的XAML代碼:
Handle first event
當(dāng)我們點(diǎn)擊Image的時(shí)候,發(fā)發(fā)生冒泡路由事件,會(huì)一層層的向外傳遞,傳遞順序: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允許終止事件的冒泡或者終止隧道過程 // 設(shè)置Handled=True,事件就不會(huì)繼續(xù)傳遞了 e.Handled = (bool)chkHandle.IsChecked; } private void cmdClear_click(object sender, RoutedEventArgs e) { eventCounter = 0; lstMessage.Items.Clear(); } } }
運(yùn)行程序,輸出結(jié)果如下:
可以看到:輸出結(jié)果就是按照我們上面的順序輸出的。我們把Grid的MouseUp事件去掉,在看輸出結(jié)果:
這時(shí)就沒有Grid控件了。勾選下面的復(fù)選框,在執(zhí)行結(jié)果:
這時(shí)只有Image控件被觸發(fā)了,其它控件的MouseUp被終止了。
2、隧道路由事件
隧道路由事件跟冒泡路由事件一樣,都是在包含層次中,但是隧道路由事件的傳遞方向跟冒泡路由事件正好相反:隧道路由事件首先是從根元素上被觸發(fā),然后從每一個(gè)元素向下沿著樹傳遞,直到到達(dá)根元素為止(或者直到到達(dá)處理程序把事件標(biāo)記為已處理為止)。
冒泡路由事件是向上傳遞,隧道路由事件是向下傳遞。
隧道路由事件在事件到達(dá)恰當(dāng)?shù)目丶埃瑸轭A(yù)覽事件提供了機(jī)會(huì)。首先是在窗口級(jí)別上,也就是頂級(jí),然后是更具體的容器,最后直到到達(dá)按下鍵時(shí)具有焦點(diǎn)的元素。我們很容易就可以識(shí)別隧道事件,因?yàn)樗麄兌际且詥卧~Preview開頭的。WPF通常可以成對(duì)的定義冒泡路由事件和隧道路由事件。如果我們找到一個(gè)MouseUp的冒泡路由事件,還可以找到一個(gè)PreviewMouseUp的隧道路由事件。隧道路由事件總是在冒泡路由事件之前被觸發(fā)。
XAML代碼如下;
后臺(tái)代碼如下:
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被點(diǎn)擊"); } private void grid_PreviewMouseDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("grid被點(diǎn)擊"); } private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e) { MessageBox.Show("button被點(diǎn)擊"); } } }
程序運(yùn)行效果:
特別值得注意的是:管道事件按照慣例,他們的名字中都有一個(gè)preview前綴,一般來說管道事件都有他的配對(duì)的冒泡事件,例如:PreviewMouseDown和MouseDown就是配對(duì)事件,如果同時(shí)存在的話,那么就會(huì)先執(zhí)行管道事件然后才執(zhí)行配對(duì)的冒泡事件。當(dāng)然e.Handled=true,依然能夠阻斷事件。
看下面的示例代碼:
Handle first event
后端事件代碼:
using System.Windows; namespace 路由事件 { ////// Window1.xaml 的交互邏輯 /// public partial class Window1 : Window { public Window1() { InitializeComponent(); } protected int eventCounter = 0; private void SomeKeyPressed(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}" + $"Event:{e.RoutedEvent}"; lstMessage.Items.Add(message); // Handled允許終止事件的冒泡或者終止隧道過程 // 設(shè)置Handled=True,事件就不會(huì)繼續(xù)傳遞了 e.Handled = (bool)chkHandle.IsChecked; } private void cmdClear_click(object sender, RoutedEventArgs e) { eventCounter = 0; lstMessage.Items.Clear(); } } }
運(yùn)行結(jié)果:
可以看到:執(zhí)行順序是從頂級(jí)元素到最里層的元素。
注意:如果要使用冒泡路由事件,只需要將PreviewKeyDown改為KeyDown即可。
3、直接策略
事件僅僅在源元素上觸發(fā),這個(gè)與普通的.Net事件的行為相同,不同的是這樣的事件仍然會(huì)參與一些路由事件的特定機(jī)制,如事件觸發(fā)器等。
該事件唯一可能的處理程序是與其掛接的委托。?
路由事件的事件處理程序的簽名(即方法的參數(shù)):
他與通用的.net事件處理程序的模式一致,也有兩個(gè)參數(shù):第一個(gè)為:System.Object對(duì)象,名為sender,第二個(gè)參數(shù)(一般名為e)是一個(gè)派生于System.EventArgs的類。sender參數(shù)就是該處理程序被添加的元素,參數(shù)e是RoutedEventArgs的一個(gè)實(shí)例提供了4個(gè)有用的屬性:
- Source---邏輯樹中開始觸發(fā)該事件的的元素。
- originalSource--可視樹中一開始觸發(fā)該事件的元素。
- handled---布爾值,設(shè)置為true表示事件已處理,在這里停止。
- RoutedEvent---真正的路由事件對(duì)象,(如Button.ClickEvent)當(dāng)一個(gè)事件處理程序同時(shí)用于多個(gè)路由事件時(shí),它可以有效地識(shí)別被出發(fā)的事件。
原文鏈接:https://www.cnblogs.com/dotnet261010/p/6368791.html
相關(guān)推薦
- 2024-07-18 spring @retryable不生效的一種場(chǎng)景
- 2022-12-01 SQL?Server修改數(shù)據(jù)的幾種語(yǔ)句詳解_MsSql
- 2022-04-06 .Net使用加密升級(jí)數(shù)據(jù)安全_實(shí)用技巧
- 2022-08-03 C#中POST接口formdata傳參模板的記錄_C#教程
- 2022-10-30 移動(dòng)web開發(fā)技能之touch事件詳解_IOS
- 2022-07-25 C++數(shù)據(jù)結(jié)構(gòu)之雙向鏈表_C 語(yǔ)言
- 2022-05-27 .Net動(dòng)態(tài)生成controller遇到的坑_實(shí)用技巧
- 2022-07-09 Dockerfile文件介紹
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支