天天看點

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:中斷事件路由      所有的路由事件都共享一個公共的事件資料基類

RoutedEventArgs

定義了一個采用布爾值的

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

方法注冊一個

。按照約定,

靜态字段名稱應當以字尾 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”。

繼續閱讀