WPF routed event

WPF routed event

在 .NET event 上,WPF 又加入了一些新的概念。一個單純的 .NET event 只會在來源元件上被發起。而 WPF event 加入了 tunneling 及 bubbling 的概念:

1. tunneling: event 一開始在 root 被發起,然後沿著 logic tree 往下移動,一直到來源元件為止。tunnel 是隧道的意思,因此引申 event 由上往下。
2. bubbling: event 一開始在來源元件發起,然後沿著 logic tree 往 root 方向移動,一直到 root 為止。bubble 泡沫是往上飄的,引申 event 由下往上。
3. direct: 跟 .NET event 差不多,event 直接在來源元件發起。

WPF 中所有 UI element 都是繼承於 UIElement,它定義了許多關於鍵盤、滑鼠及觸控裝置的事件。大多都是 bubbling event,但有些 event 會有成對的 tunneling evnet 與之配對。這些 tunneling event 都以 Preview 做開頭。例如 MouseMove event 是一個 bubbling event,而 PreviewMouseMove 則是一個 tunneling event。tunneling event 會比 bubbling event 更早發出。此例中,PreviewMouseMove 會比 MouseMove 更早發出。

這樣設計有什麼用處呢?假使我們要在一個 TextBox 中限定輸入字元,假如我們在 KeyDown 中偵測字元,其實這時候字元已經顯示出來了。我們能做的就是刪除該字元。如果配合 PreviewKeyDown event,這時候字元尚未顯示,我們可以針對不允許的字元 event,設定為 "handled"。如此接下來的 bubbling event 就不會被發起。

設定 "handled" 是在 event handler 中處理。event handler signature 提供二個參數,第一個是 System.Object,一般命名為 sender;第二個是一個繼承自 System.EventArgs 的 class(端看是什麼 event handler 而不同),一般命名為 e。這個 e 提供了 4 個 property:

1. Source
2. OriginalSource
3. Handled
4. RoutedEvent

其中第 3 個 property 設為 true,就代表這個 event 是 "handled"。event 會停止 bubbling or tunneling。然而事實上,就算設定 event 為 "handled",其它 control 如果有透過 AddHandler 將此 event 加入,它還是有辦法可以收的到:

public AboutDialog()
{
   InitializeComponent();
   this.AddHandler(Window.MouseRightButtonDownEvent,
      new MouseButtonEventHandler(AboutDialog_MouseRightButtonDown), true);
}

AddHandler 最後一個參數設為 true 就可以收到 handled event。因此實際上,handled event 並沒有停止移動,只不過預設的 event handler 只能看見 unhandled event 罷了。

我們應該避免處理 handled event,因為它會被設為 handled 一定有它的原因。要在 event handler 之前處理,可以透過 Preview 版本的 event handler。


Attached Event

類似 attached property 一樣,在不支援某些 event 的 control 上,也可以透過 attached event 來收到該 event。比如說在 window 上,我們可以這樣寫:

<window ... ListBox.SelectionChanged="...">

事實上 window 不支援 ListBox 的 SelectionChanged event。但透過 attached event,window 還是可以收到這個事件。attached event 實作上就是透過 AddHandler 來完成。

留言

熱門文章