WPF路由事件

藍白永恆發表於2022-04-27

​    這節講一下WPF中的路由事件(Routed Event)

 

【什麼是事件】

    在瞭解路由事件前,我們應先來了解一下什麼是事件(Event)

    在Windows系統中,像滑鼠單擊,雙擊,移動這樣的,都是在觸發著一個個事件,事件代表著使用者在Windows上的一個動作,相當於使用者給系統交代了一個任務讓它去執行。本質上事件就是條資訊資料,這條資料有對事件的描述,以及攜帶著事件的引數,這些引數可以看做是事件的“Metadata”,比如你點選滑鼠左鍵,會觸發MouseLeftDown和MouseLeftUp這兩個事件,它們的引數中就攜帶了滑鼠在螢幕的點選位置(X,Y值)等等資訊。

    我們回到程式設計概念中,在事件這個模型中,我們要理解以下三個跟事件有關的抽象:

  • 事件的擁有者:事件的擁有者就是事件的觸發者,比如按鈕被點選,那麼按鈕就是事件的擁有者;

  • 事件的響應者:事件的響應者就是事件的處理者,比如我們在winform後置程式碼中宣告的一個一個事件處理方法,擁有事件處理方法的from體就是事件的響應者;

  • 事件訂閱關係:要想一個事件被處理,需要讓事件的響應者去訂閱事件擁有者的事件,在winfrom中這一操作被具象化為在“小閃電”操作欄中對對應的事件關聯上後置程式碼中的事件處理器。

    如果說事件的擁有者和響應者是河的兩岸,那事件的訂閱關係就是連線兩岸的橋,讓事件的 擁有者通過橋把事件資料交代給事件的響應者,而橋並不是唯一的,事件的訂閱可以是多個,它是一對n(n>=0)的關係。

    當然,上述事件模型也有其弊端:

  • 事件的響應者必須要顯式訂閱事件才能生效

  • 事件的擁有者必須能訪問到事件的響應者,這樣才能建立訂閱關係

    所以我們能瞭解到,原始的事件模型,對於訂閱關係的建立有嚴格的要求,因此,微軟在WPF中推出了路由事件,它使得事件可以不再以訂閱關係建立,下面來了解一下。

 

【路由事件】

  提到路由事件,首先一點,什麼是路由呢?這裡引入《深入淺出WPF》一書中對路由的解釋:“起點與終點間有若干個中轉站,從起點出發後經過每個中轉站時要做出選擇,最終以正確(比如最短或者最快)的路徑到達終點。” 路由描述的就是這樣的一個過程。

     路由事件,是指事件的擁有者和響應者不必建立訂閱關係,擁有者只管激發事件,響應者通過在自身設定事件監聽器去監聽對應的事件,並可以決定事件是否繼續傳播,如果說原始事件是兩個人竊竊私語的話,那路由事件就是一隊人挨個傳話。當事件響應者通過事件監聽器監聽到某個事件的發生,通過事件攜帶的引數可以獲取到事件的來源,從而做出判斷該事件是否是自己關心的某個控制元件激發的,如果是,可以處理並停止事件的傳播,如果不是,則放行不予理睬。

    請設想如下圖所示的一個XAML控制元件層級關係:

    藍色代表Window控制元件,其內部有兩個按鈕和一個Grid佈局,按鈕2在Grid佈局中,當按鈕1激發單擊事件後,該事件的傳播路徑為:

  按鈕1-->Window

    當按鈕2激發單擊事件後,該事件的傳播路徑為:

  按鈕2-->Grid-->Window

 

 

【如何使用路由事件】

  下面來學習一下如何使用事件監聽器監聽路由事件,請看如下程式碼:

    XAML頁面結構是名為grid的Grid佈局中有個點選按鈕。我們在後置程式碼中使用AddHandler方法設定事件監聽器,該方法的第一個引數是指定監聽的路由事件型別物件,第二個引數是指定事件處理器,處理器方法由RoutedEventHandler物件包裝,當按鈕點選時,在輸出視窗中輸出了“監聽到了btn_click的事件”字樣。此處要注意,跟原始事件處理器不同的是,路由事件處理器的第一個引數sender,是監聽事件的控制元件物件在此處就是grid物件,而我們要獲取是誰激發的事件則是根據第二個引數e的OriginalSource屬性。

    當我們捕獲到關心的事件時,控制事件不再繼續傳播該怎麼做呢,事件處理器的第二個引數e有個Handled屬性,該屬性是個bool值,設定其為true即可。

    當然,事件監聽器也可以從XAML程式碼中指定:

    通過為ButtonBase(Button的父類)的Click路由事件處理器繫結方法,來實現單擊事件的監聽。從ButtonBase原始碼中可以找到如下圖所示的路由事件處理器,該型別跟AddHandler方法的第二個引數型別一致。

 

相關文章