WPF 路由事件 Event Routing
阿新 • • 發佈:2019-02-03
1.路由事件介紹
之前介紹了WPF的新的依賴屬性系統,本篇將介紹更高階的路由事件,替換了之前的.net普通事件。相比.net的事件,路由事件具有更強的傳播能力,支援向上冒泡和向下隧道傳播。路由事件允許源自某個元素的事件由另一個元素引發。
2.路由事件定義
WPF事件模型和WPF屬性模型非常類似。都是隻讀的靜態欄位。
[DefaultEvent("Click")] [Localizability(LocalizationCategory.Button)] public abstract class ButtonBase : ContentControl, ICommandSource { public static readonly RoutedEvent ClickEvent; }
3.路由事件註冊
和WPF事件模型的註冊與屬性幾乎一樣,使用EventManager.RegisterRoutedEvent()來進行註冊。可以檢視該函式的說明:
與依賴屬性同樣,在一個靜態建構函式中註冊:// // 摘要: // 向 Windows Presentation Foundation (WPF) 事件系統註冊新的路由事件。 // // 引數: // name: // 路由事件的名稱。該名稱在所有者型別中必須是唯一的,並且不能為 null 或空字串。 // // routingStrategy: // 作為列舉值的事件的路由策略。 // // handlerType: // 事件處理程式的型別。該型別必須為委託型別,並且不能為 null。 // // ownerType: // 路由事件的所有者類型別。該型別不能為 null。 // // 返回結果: // 新註冊的路由事件的識別符號。現在可將該識別符號物件儲存為類中的靜態欄位,然後將其用作將處理程式附加到事件的方法的引數。路由事件識別符號也用於其他事件系統 // APIs。 public static RoutedEvent RegisterRoutedEvent(string name, RoutingStrategy routingStrategy, Type handlerType, Type ownerType);
static ButtonBase()
{
ButtonBase.ClickEvent=EventManager.RegisterRoutedEvent
("Click",RoutingStrategy.Bubble,typeof(RoutedEventHandler),typeof(ButtonBase));
}
4.路由事件包裝
路由事件通過普通的.net事件進行包裝。從而使所有.net語言都能訪問它們。事件包裝器可以使用AddHandler()和RemoveHandler()方法新增和刪除已註冊的呼叫程式。
AddHandler與RemoveHandler在基類UIElement中定義,每個WPF元素都繼承它們。
// 摘要: // 在單擊 System.Windows.Controls.Button 時發生。 [Category("Behavior")] public event RoutedEventHandler Click { add { base.AddHandler(ButtonBase.ClickEvent, value); } remove { base.RemoveHandler(ButtonBase.ClickEvent, value); } }
5.路由事件共享
public RoutedEvent AddOwner(Type ownerType);
將路由事件關聯另一個所有者型別,並啟用事件及其處理的路由
WPF中所有控制元件的基類UIElement型別就共享了MouseUp事件。MouseUp事件是在System.Windows.Input.Mouse類定義的。UIElement只是通過了AddOwner()方法重用了MouseUp事件。
UIElement.MouseUpEvent=Mouse.MouseUpEvent.AddOwner(typeof(UIElement));
6.引發路由事件
RoutedEventArgs args = new RoutedEventArgs(ButtonBase.ClickEvent,this);
base.RaiseEvent(args);
RaiseEvent方法負責為每個已經通過AddHandler()方法註冊的呼叫程式引發路由事件。 這裡先貼出來RoutedEventArgs的建構函式。
// 引數:
// routedEvent:
// System.Windows.RoutedEventArgs 類的此例項的路由事件識別符號。
//
// source:
// 將在處理事件時報告的備用源。這將預先填充 System.Windows.RoutedEventArgs.Source 屬性。
public RoutedEventArgs(RoutedEvent routedEvent, object source);
RoutedEventArgs有下面四個屬性:1.Source 指定了引發事件的物件
2.OriginalSource 指出了最初是什麼物件引發了事件。比Source更深一層。
3.Handled 使用者來終止事件的冒泡或者隧道過程。如果一個控制元件將Handled設定為true,剛這個事件就不會繼續傳遞下去。
4.RoutedEvent 獲取或設定與此RoutedEventArgs 例項關聯的路由事件。
通過使用RoutedEventArgs我們可以為事件提供相應的源。在WPF中,如果一個事件確實需要傳遞額外的資訊,我們可以自定義一個物件,繼承自RoutedEventArgs。例如WPF中常見的MouseEventArgs。
7.關聯路由事件
將後臺事件處理程式與前臺元素相關聯有很多種方法。最常見的就是為Xaml新增事件特性。如:
<Button x:Name="button1" Content="ok" Width="80" Height="40" Click="button1_Click"/>
或者不需要元素名,如:<Button Content="ok" Width="80" Height="40" Click="OK_Click"/>
也可以在後臺程式碼中進行連線事件,如:img.MouseUp+=new MouseButtonEventHandler(img_MouseUp);
C#還支援隱式地建立委託物件:img.MouseUp+=img_MouseUp;
上述方法實質上都是呼叫了事件包裝器(小節4)。我們可以直接呼叫UIElement.AddHandler()方法來直接連線事件。如:
img.AddHandler(Image.MouseUpEvent,new MouseButtonEventHandler(img_MouseUp));
8.斷開關聯
斷開與路由事件的關聯主要有兩種方法:1.-=運算子:
img.MouseUp-=img_MouseUp;
2.使用UIElement.RemoveHandler()方法:
img.RemoveHandler(Image.MouseUpEvent,new MouseButtonEventHandler(img_MouseUp));
9.最後的例子
與之前一樣,以一個小例子結尾,來幫助理解。程式碼不多,直接貼上來
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.AddHandler(BigDog.TestEvent, new RoutedEventHandler(this.TestHandler));
}
private void TestHandler(object sender, RoutedEventArgs e)
{
MessageBox.Show((e.OriginalSource as BigDog).Name.ToString());
e.Handled = true;
}
private void button1_Click(object sender, RoutedEventArgs e)
{
BigDog dear = new BigDog { Name="Dear"};
RoutedEventArgs args = new RoutedEventArgs(BigDog.TestEvent, dear);
//引發路由事件
this.button1.RaiseEvent(args);
}
}
public class BigDog
{
public string Name { get; set; }
//定義路由事件
public static readonly RoutedEvent TestEvent = EventManager.RegisterRoutedEvent
("Test", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(BigDog));
}