1. 程式人生 > >WPF的路由事件、冒泡事件、隧道事件(預覽事件)

WPF的路由事件、冒泡事件、隧道事件(預覽事件)

原文: WPF的路由事件、冒泡事件、隧道事件(預覽事件)

本文摘要:

1:什麼是路由事件;

2:中斷事件路由;

3:自定義路由事件;

4:為什麼需要自定義路由事件;

5:什麼是冒泡事件和預覽事件(隧道事件);

1:什麼是路由事件

    WPF中的事件為路由事件,所謂路由事件,MSDN定義如下:

    功能定義:路由事件是一種可以針對元素樹中的多個偵聽器(而不是僅針對引發該事件的物件)呼叫處理程式的事件。

    實現定義:路由事件是一個 CLR 事件,可以由 RoutedEvent 類的例項提供支援並由 Windows Presentation Foundation (WPF) 事件系統來處理。

    但這兩類定義都比較抽象,我們來看更具體的定義:

       <Border Height="50" Width="250" BorderBrush="Gray" BorderThickness="1" > <StackPanel Background="LightGray" Orientation="Horizontal" MouseUp="StackPanel_MouseUp"> <TextBlock Name="YesTB" Width="50" MouseUp="YesTB_MouseUp" Background="Blue" >Yes</TextBlock> </StackPanel> </Border>

    在這個例子中,事件的事件路由為:

TextBlock -->StackPanel-->Border —>...

2:中斷事件路由      所有的路由事件都共享一個公共的事件資料基類 RoutedEventArgsRoutedEventArgs 定義了一個採用布林值的 Handled 屬性。 Handled 屬性的目的在於,允許路由中的任何事件處理程式通過將 Handled 的值設定為 true 來將路由事件標記為“已處理”。

        private void StackPanel_MouseUp(object sender, MouseButtonEventArgs e)
        {
            MessageBox.Show("Panel");
        }

        private void YesTB_MouseUp(object sender, MouseButtonEventArgs e) { MessageBox.Show("button"); e.Handled = true; }

    在上面的例子中,將不再觸發StackPanel_MouseUp事件。

3:自定義路由事件

      如下面的示例所示,首先使用 RegisterRoutedEvent 方法註冊一個 RoutedEvent。按照約定,RoutedEvent 靜態欄位名稱應當以後綴 Event 結束。在本示例中,事件的名稱是 Tap,事件的路由策略是 Bubble。在註冊呼叫之後,可以為該事件提供新增和移除公共語言執行時 (CLR) 事件訪問器。

      請注意,儘管該事件在本特定示例中是通過 OnTap 虛方法引發的,但您引發事件的方式或者事件響應更改的方式取決於您的需要。

      還要注意,本示例主要實現 Button 的一整個子類;該子類是作為單獨的程式集構建的,之後將在單獨的可擴充套件應用程式標記語言 (XAML) 頁上例項化為一個自定義類。這是為了說明這樣一個概念:建立子類的控制元件可以插入到由其他控制元件組成的樹中,在這種情況下,這些控制元件上的自定義事件具有與任何固有的 Windows Presentation Foundation (WPF) 元素完全相同的事件路由功能。

public class MyButtonSimple: Button
{
    // Create a custom routed event by first registering a RoutedEventID
    // This event uses the bubbling routing strategy
    public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent( "Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple)); // Provide CLR accessors for the event public event RoutedEventHandler Tap { add { AddHandler(TapEvent, value); } remove { RemoveHandler(TapEvent, value); } } // This method raises the Tap event void RaiseTapEvent() { RoutedEventArgs newEventArgs = new RoutedEventArgs(MyButtonSimple.TapEvent); RaiseEvent(newEventArgs); } // For demonstration purposes we raise the event when the MyButtonSimple is clicked protected override void OnClick() { RaiseTapEvent(); } }
<Window  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:custom="clr-namespace:SDKSample;assembly=SDKSampleLibrary" x:Class="SDKSample.RoutedEventCustomApp" > <Window.Resources> <Style TargetType="{x:Type custom:MyButtonSimple}"> <Setter Property="Height" Value="20"/> <Setter Property="Width" Value="250"/> <Setter Property="HorizontalAlignment" Value="Left"/> <Setter Property="Background" Value="#808080"/> </Style> </Window.Resources> <StackPanel Background="LightGray"> <custom:MyButtonSimple Name="mybtnsimple" Tap="TapHandler">Click to see Tap custom event work</custom:MyButtonSimple> </StackPanel> </Window>

4:為什麼需要自定義路由事件

      一直到目前看來,我們都不太需要自定義的路由事件。但是,在我們建立自定義控制的時候,建立一些和業務相關的路由事件,就顯得很有必要。

      如,建立一個線上考試中的題型展示控制元件,可以為該控制元件設計一個自定義事件,為“提交”。這樣一來,這個題型控制元件不僅僅只有一些通用事件,還可以看上去更“業務”。

5:什麼是冒泡事件和預覽事件(隧道事件)

 路由事件實際上分兩類:冒泡事件和預覽事件(隧道事件)。上文中的例子就是冒泡事件。

     冒泡事件是WPF路由事件中最為常見,它表示事件從源元素擴散(傳播)到可視樹,直到它被處理或到達根元素。這樣您就可以針對源元素的上方層級物件處理事件。例如,您可向嵌入的 Grid 元素附加一個 Button.Click 處理程式,而不是直接將其附加到按鈕本身。氣泡事件有指示其操作的名稱(例如,MouseDown)。

     隧道事件採用另一種方式,從根元素開始,向下遍歷元素樹,直到被處理或到達事件的源元素。這樣上游元素就可以在事件到達源元素之前先行擷取並進行處理。根據命名慣例,隧道事件帶有字首 Preview(例如 PreviewMouseDown)。

     在本文一開始的例子中,如果我們將MouseUP,改為PreviewMouseUP,效果會如何呢。

區別:

     冒泡事件:在YesTB上點選,首先彈出“button”,再彈出“panel”。

     預覽事件(隧道事件)事件:在YesTB上點選,首先彈出“panel”,再彈出“button”。

     看到了這點區別,那麼我們加入e.Handled=true的時機也要不同。首先,

     冒泡事件例子中:e.Handled=true加在YesTB_PreviewMouseUp中,加入後,點選YesTB,將只彈出“button”。

     預覽事件(隧道事件)例子中:e.Handled=true家在StackPanel_PreviewMouseUp中,加入後,點選YesTB,將只彈出“panel”。