WPF,Silverlight与XAML读书笔记第八 - WPF新概念之三路由事件
說明:本系列基本上是《WPF揭秘》的讀書筆記。在結構安排與文章內容上參照《WPF揭秘》的編排,對內容進行了總結并加入一些個人理解。
?
????? 路由事件是專門設計用于在元素樹中使用的事件。當路由事件觸發后,其可以向上或向下遍歷可視樹和邏輯樹,并且持續在每個元素上觸發。
????? 我們以按鈕為例,說明路由事件的作用。在WPF之前的Windows客戶端技術WinForm中按鈕僅僅就是一個System.Windows.Forms命名空間下的Button類的對象。對其的點擊等操作可以直接捕獲,但是WPF中展現給你的按鈕的組成可能會很復雜。其可能包括ButtonChrome或者TextBlock等可視子元素或者包含Rectangle等邏輯子元素,這時候對一個按鈕的點擊(MouseLeftButtonDown事件或KeyDown事件)可能實際發生在其可視子元素或邏輯子元素上,這時使用路由事件,由于其遍歷可視樹/邏輯樹,Button的Click(路由)事件最終會被觸發。
?
路由事件的實現
????? 同依賴屬性,路由事件也只有XAML語言天生支持,在傳統過程式代碼中注冊路由事件的代碼如下(以Button的Click路由事件為例)。首先注意的是,路由事件的約定名稱是在傳統事件名稱的后面加上Event后綴(類似依賴屬性的Property后綴)。
示例代碼:
//聲明路由事件 public?static?readonly?RoutedEvent?ClickEvent;首先在Button類中聲明這個路由事件。接著,
//注冊路由事件 Button.ClickEvent?=?EventManager.RegisterRoutedEvent( "Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Button) );在Button類的靜態構造函數中初始化這個路由事件。
最后添加可選的.NET事件包裝器
//(可選).NET事件包裝器 public?event?RoutedEventHandler?Click {add?{???AddHandler(Button.ClickEvent,?value);?}remove?{???RemoveHandler(Button.ClickEvent,?value);?} }觸發路由事件的代碼(可以位于OnMouseLeftButtonDown處理程序中)
RaiseEvent(new?RoutedEventArgs(Button.ClickEvent,?this));分析:
RegisterRoutedEvent方法中傳入的第一個參數定義了一個普通的.NET事件,事件包裝器對這個事件進行包裝。這樣可以使過程式代碼中事件使用更接近原有模式,并且XAML中也可以更方便的添加事件處理程序。
?
.NET事件包裝器與內部實現
事件包裝器內部調用的AddHandler與RemoveHandler方法位于繼承自DependencyObject對象的UIElement對象。這兩個方法分別可以向一個適當的路由事件添加一個委托對象及由路由事件移除一個委托對象。事件包裝器中只能調用AddHandler和RemoveHandler,不能做它用,這點與屬性包裝器類似。
?
路由策略
在前文注冊路由事件的代碼中你已經見過RoutingStrategy這個枚舉。與此相關的就是路由策略的概念,路由策略指的是事件觸發遍歷元素樹的方式。有以下三種:
-
Tunneling(管道傳遞)
事件首先在根元素上被觸發,然后向下沿著樹中每一個元素傳遞,直到到達源元素為止(或者直到處理程序把事件標記為已處理為止)。
-
? Bubbling(冒泡傳遞)
事件首先在源元素上被觸發,然后向上沿著樹中每一個元素傳遞,直到到達根元素為止(或者直到處理程序把事件標記為已處理為止)。
-
Direct(直接)
事件僅在源元素上觸發。這與普通.NET事件的行為基本相同。不同之處在于此類事件仍會參與事件觸發器等路由事件的特定機制。
?
路由事件處理程序
???????? 路由事件處理程序(RoutedEventHandler)與普通的.NET事件的處理程序的設計遵循相同的模式:第一個參數為名為Sender的System.Object對象表示流處理程序被添加到的元素。第二個元素是名為e(通常習慣)的派生自RoutedEventArgs或其子類(這些類都派生自System.EventArgs類)的對象。這個對象有4個屬性,通過它們變向擴展了提供給路由事件的參數。
-
Source:邏輯樹中第一個觸發該事件的元素。
-
OriginalSource:可視樹中第一個觸發該事件的元素(如點擊Button首先觸發的可視樹為ButtonChrome,這個及前者的屬性多用于區分鼠標事件之類的物理事件)。
-
Handled:布爾值,設置為true來標記事件為已處理,用于停止Tunneling或Bubbling事件在樹上的觸發。
-
RoutedEvent:真正的路由事件對象(如Button.ClickEvent),當多個路由事件共享一個事件處理函數時,此屬性可以用來區分這個事件。
?
路由事件的作用
?????? 路由事件定義于UIElement類中,主要用于鍵盤,鼠標,觸控筆之類觸發的物理事件。而且這些路由事件往往是成對定義的即對于一個物理事件存在一個管道事件與一個冒泡事件相對應。其中管道事件的慣例是以Preview前綴開頭。這個管道事件會在對應的冒泡事件之前觸發,WPF的內部元素也只會響應冒泡事件。所以你可以在管道事件發生的過程中預覽事件或者采取停止事件等措施。(實例:對于TextBox可以處理其KeyDown的管道事件(PreviewKeyDown事件)來嚴格限制其中輸入的內容)。如果內容不合要求則在PreviewKeyDown事件發生的過程中標記事件為已處理,這樣KeyDown冒泡事件將不再觸發,則對TextBox的操作不會有任何效果。
?????? 注意在冒泡事件由source逐級向聲明其的對象觸發的過程中,可以被其中的接受事件的對象處理,并終止此冒泡事件。
?
最佳控制事件的方式
????? 在路由事件的Preview版本中添加事件處理程序。
路由事件(管道或冒泡)的終止都是表面的,其只是被標記為已處理,但事件傳遞會繼續,只是事件處理程序只處理沒有標記為已處理的事件。
路由事件既可以沿可視樹傳遞,也可以沿邏輯樹傳遞。
?
附加事件
????? 當樹中(邏輯樹或可視樹)的每個元素都有路由事件時,路由事件(管道類型與冒泡類型)自然可以在其中傳遞。有了附加事件,事件傳遞也可以在一個沒有定義這些事件的元素上進行。由于定義附加事件的元素本身不支持此事件,所以要在此附加事件上定義處理此事件(路由事件)的方法的名稱。
示例:如下XAML代碼將ListBox的SelectionChanged路由事件與Button的Click路由事件作為附加事件提供給window元素。
<Window ...ListBox.SelectionChanged="ListBox_SelectionChanged" Button.Click="Button_Click">與這些XAML對應的過程代碼實現:
this.AddHandler(ListBox.SelectionChangedEvent,?new?SelectionChangedEventHandler(ListBox_SelectionChanged)); this.AddHandler(Button.ClickEvent,?new?RoutedEventHandler(Button_Click));需要注意的是這些過程代碼應置于InitializeComponent()后。
?
本文完
?
參考:
《WPF揭秘》
轉載于:https://www.cnblogs.com/lsxqw2004/archive/2011/09/08/4554476.html
總結
以上是生活随笔為你收集整理的WPF,Silverlight与XAML读书笔记第八 - WPF新概念之三路由事件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ProgressBar 类
- 下一篇: 结构化程序设计03 - 零基础入门学习D