共用方式為


預覽事件 (WPF.NET)

預覽事件,也稱為通道事件,是路由事件,從應用程式根元素向下周游專案樹狀結構到引發事件的元素。 引發事件的 元素會回報為 Source 事件資料中的 。 並非所有事件案例都支援或需要預覽事件。 本文說明預覽事件存在的位置,以及應用程式或元件如何與其互動。 如需如何建立預覽事件的資訊,請參閱 如何建立自定義路由事件

必要條件

本文假設您已瞭解路由事件,而且您已閱讀 路由事件概觀。 若要遵循本文中的範例,如果您熟悉可延伸的應用程式標記語言(XAML),並知道如何撰寫 Windows Presentation Foundation (WPF) 應用程式,它很有説明。

標示為已處理的預覽事件

將預覽事件標示為事件數據中所處理時,請小心。 將預覽事件標示為在引發之專案以外的項目上處理,可以防止引發預覽事件的項目處理事件。 有時會刻意將預覽事件標示為已處理。 例如,複合控件可能會隱藏個別元件所引發的事件,並以完整控件所引發的事件取代它們。 控制件的自定義事件可以根據元件狀態關聯性提供自訂事件數據和觸發程式。

針對 輸入事件,事件數據會由每個事件的預覽和非預覽(升階)對等項目共用。 如果您使用預覽事件類別處理程式將輸入事件標示為已處理,通常不會叫用反升輸入事件的類別處理程式。 或者,如果您使用預覽事件實例處理程式將事件標示為已處理,則通常不會叫用升階輸入事件的實例處理程式。 雖然您可以將類別和實例處理程式設定為叫用,即使事件標示為已處理,該處理程式組態並不常見。 如需類別處理及其與預覽事件關聯的詳細資訊,請參閱 將路由事件標示為已處理和類別處理

注意

並非所有預覽事件都是 通道 事件。 例如,PreviewMouseLeftButtonDown輸入事件會透過元素樹狀結構追蹤向下路由,但是由路由中的每個事件引發和重新引發UIElement的直接路由事件。

依控件處理事件隱藏

某些複合控件會隱藏元件層級的輸入事件,以自定義的高階事件取代它們。 例如,WPF ButtonBase 會將反升輸入事件標示 MouseLeftButtonDown 為在其 OnMouseLeftButtonDown 方法中處理,並引發 Click 事件。 事件 MouseLeftButtonDown 及其事件數據仍會沿著元素樹狀目錄路由繼續,但由於事件在事件數據中標示為 Handled ,因此只會叫用設定為回應已處理事件的處理程式。

如果您想要讓應用程式根目錄的其他元素處理標示為已處理的路由事件,您可以:

  • 呼叫 方法並將 參數handledEventsToo設定為 trueUIElement.AddHandler(RoutedEvent, Delegate, Boolean)以附加處理程式。 此方法需要在程式代碼後置中附加事件處理程式,才能取得要附加至之專案的對象參考。

  • 如果標示為已處理的事件是反升事件,則如果有的話,請附加對等預覽事件的處理程式。 例如,如果控件隱藏 MouseLeftButtonDown 事件,您可以改為附加事件的處理程式 PreviewMouseLeftButtonDown 。 此方法僅適用於實作通道和升階路由策略並共用事件數據的基底元素輸入事件。

下列範例會實作名為 的粗細自定義控制項,其中包含 componentWrapper TextBox。 控制項會新增至 StackPanel 具名 outerStackPanel的 。

<StackPanel Name="outerStackPanel"
    VerticalAlignment="Center"
    custom:ComponentWrapper.CustomKey="Handler_PrintEventInfo"
    TextBox.KeyDown="Handler_PrintEventInfo"
    TextBox.PreviewKeyDown="Handler_PrintEventInfo" >
    <custom:ComponentWrapper
        x:Name="componentWrapper"
        TextBox.KeyDown="ComponentWrapper_KeyDown"
        custom:ComponentWrapper.CustomKey="Handler_PrintEventInfo"
        HorizontalAlignment="Center">
        <TextBox Name="componentTextBox" Width="200" KeyDown="Handler_PrintEventInfo" />
    </custom:ComponentWrapper>
</StackPanel>

每當發生擊鍵時,KeyDown控件componentWrapper會接聽其TextBox元件引發的反升事件。 在該發生時, componentWrapper 控件:

  1. KeyDown將反升路由事件標示為已處理以隱藏事件。 因此,只會觸發程式outerStackPanel代碼後置中設定以回應已處理KeyDown事件的處理程式。 outerStackPanel不會叫用 XAML 中為KeyDown事件附加的處理程式。

  2. 引發名為 CustomKey的自定義反升路由事件,這會觸發 outerStackPanel 事件的處理程式 CustomKey

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Attach a handler on outerStackPanel that will be invoked by handled KeyDown events.
        outerStackPanel.AddHandler(KeyDownEvent, new RoutedEventHandler(Handler_PrintEventInfo), 
            handledEventsToo: true);
    }

    private void ComponentWrapper_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
    {
        Handler_PrintEventInfo(sender, e);

        Debug.WriteLine("KeyDown event marked as handled on componentWrapper.\r\n" +
            "CustomKey event raised on componentWrapper.");

        // Mark the event as handled.
        e.Handled = true;

        // Raise the custom click event.
        componentWrapper.RaiseCustomRoutedEvent();
    }

    private void Handler_PrintEventInfo(object sender, System.Windows.Input.KeyEventArgs e)
    {
        string senderName = ((FrameworkElement)sender).Name;
        string sourceName = ((FrameworkElement)e.Source).Name;
        string eventName = e.RoutedEvent.Name;
        string handledEventsToo = e.Handled ? " Parameter handledEventsToo set to true." : "";

        Debug.WriteLine($"Handler attached to {senderName} " +
            $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}");
    }

    private void Handler_PrintEventInfo(object sender, RoutedEventArgs e)
    {
        string senderName = ((FrameworkElement)sender).Name;
        string sourceName = ((FrameworkElement)e.Source).Name;
        string eventName = e.RoutedEvent.Name;
        string handledEventsToo = e.Handled ? " Parameter handledEventsToo set to true." : "";

        Debug.WriteLine($"Handler attached to {senderName} " +
            $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}");
    }

    // Debug output:
    //
    // Handler attached to outerStackPanel triggered by PreviewKeyDown event raised on componentTextBox.
    // Handler attached to componentTextBox triggered by KeyDown event raised on componentTextBox.
    // Handler attached to componentWrapper triggered by KeyDown event raised on componentTextBox.
    // KeyDown event marked as handled on componentWrapper.
    // CustomKey event raised on componentWrapper.
    // Handler attached to componentWrapper triggered by CustomKey event raised on componentWrapper.
    // Handler attached to outerStackPanel triggered by CustomKey event raised on componentWrapper.
    // Handler attached to outerStackPanel triggered by KeyDown event raised on componentTextBox. Parameter handledEventsToo set to true.
}

public class ComponentWrapper : StackPanel
{
    // Register a custom routed event using the Bubble routing strategy.
    public static readonly RoutedEvent CustomKeyEvent = 
        EventManager.RegisterRoutedEvent(
            name: "CustomKey",
            routingStrategy: RoutingStrategy.Bubble,
            handlerType: typeof(RoutedEventHandler),
            ownerType: typeof(ComponentWrapper));

    // Provide CLR accessors for assigning an event handler.
    public event RoutedEventHandler CustomKey
    {
        add { AddHandler(CustomKeyEvent, value); }
        remove { RemoveHandler(CustomKeyEvent, value); }
    }

    public void RaiseCustomRoutedEvent()
    {
        // Create a RoutedEventArgs instance.
        RoutedEventArgs routedEventArgs = new(routedEvent: CustomKeyEvent);

        // Raise the event, which will bubble up through the element tree.
        RaiseEvent(routedEventArgs);
    }
}
Partial Public Class MainWindow
        Inherits Window

        Public Sub New()
        InitializeComponent()

        ' Attach a handler on outerStackPanel that will be invoked by handled KeyDown events.
        outerStackPanel.[AddHandler](KeyDownEvent, New RoutedEventHandler(AddressOf Handler_PrintEventInfo),
                                     handledEventsToo:=True)
    End Sub

    Private Sub ComponentWrapper_KeyDown(sender As Object, e As KeyEventArgs)
        Handler_PrintEventInfo(sender, e)
        Debug.WriteLine("KeyDown event marked as handled on componentWrapper." &
                        vbCrLf & "CustomKey event raised on componentWrapper.")

        ' Mark the event as handled.
        e.Handled = True

        ' Raise the custom click event.
        componentWrapper.RaiseCustomRoutedEvent()
    End Sub

    Private Sub Handler_PrintEventInfo(sender As Object, e As KeyEventArgs)
        Dim senderName As String = CType(sender, FrameworkElement).Name
        Dim sourceName As String = CType(e.Source, FrameworkElement).Name
        Dim eventName As String = e.RoutedEvent.Name
        Dim handledEventsToo As String = If(e.Handled, " Parameter handledEventsToo set to true.", "")
        Debug.WriteLine($"Handler attached to {senderName} " &
                        $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}")
    End Sub

    Private Sub Handler_PrintEventInfo(sender As Object, e As RoutedEventArgs)
        Dim senderName As String = CType(sender, FrameworkElement).Name
        Dim sourceName As String = CType(e.Source, FrameworkElement).Name
        Dim eventName As String = e.RoutedEvent.Name
        Dim handledEventsToo As String = If(e.Handled, " Parameter handledEventsToo set to true.", "")
        Debug.WriteLine($"Handler attached to {senderName} " &
                        $"triggered by {eventName} event raised on {sourceName}.{handledEventsToo}")
    End Sub

    ' Debug output
    '
    ' Handler attached to outerStackPanel triggered by PreviewKeyDown event raised on componentTextBox.
    ' Handler attached to componentTextBox triggered by KeyDown event raised on componentTextBox.
    ' Handler attached to componentWrapper triggered by KeyDown event raised on componentTextBox.
    ' KeyDown event marked as handled on componentWrapper.
    ' CustomKey event raised on componentWrapper.
    ' Handler attached to componentWrapper triggered by CustomKey event raised on componentWrapper.
    ' Handler attached to outerStackPanel triggered by CustomKey event raised on componentWrapper.
    ' Handler attached to outerStackPanel triggered by KeyDown event raised on componentTextBox. Parameter handledEventsToo set to true.
End Class

    Public Class ComponentWrapper
        Inherits StackPanel

        ' Register a custom routed event with the Bubble routing strategy.
        Public Shared ReadOnly CustomKeyEvent As RoutedEvent =
            EventManager.RegisterRoutedEvent(
                name:="CustomKey",
                routingStrategy:=RoutingStrategy.Bubble,
                handlerType:=GetType(RoutedEventHandler),
                ownerType:=GetType(ComponentWrapper))

        ' Provide CLR accessors to support event handler assignment.
        Public Custom Event CustomKey As RoutedEventHandler

            AddHandler(value As RoutedEventHandler)
                [AddHandler](CustomKeyEvent, value)
            End AddHandler

            RemoveHandler(value As RoutedEventHandler)
                [RemoveHandler](CustomKeyEvent, value)
            End RemoveHandler

            RaiseEvent(sender As Object, e As RoutedEventArgs)
                [RaiseEvent](e)
            End RaiseEvent

        End Event

    Public Sub RaiseCustomRoutedEvent()
        ' Create a RoutedEventArgs instance & raise the event,
        ' which will bubble up through the element tree.
        Dim routedEventArgs As New RoutedEventArgs(routedEvent:=CustomKeyEvent)
            [RaiseEvent](routedEventArgs)
        End Sub
    End Class

此範例示範取得隱藏 KeyDown 路由事件的兩個因應措施,以叫用附加至 的 outerStackPanel事件處理程式:

  • PreviewKeyDown 事件處理程式附加至 outerStackPanel。 由於預覽輸入路由事件在對等的反升路由事件之前,範例中的處理程式會在處理程式前面KeyDown執行,PreviewKeyDown透過其共用事件數據隱藏預覽和反升事件。

  • KeyDown使用UIElement.AddHandler(RoutedEvent, Delegate, Boolean)程式代碼後置中的 方法,將 handledEventsToo 事件處理程式附加至 outerStackPanel ,並將 參數設定為 true

注意

標記所處理之輸入事件的預覽或非預覽對等專案,都是隱藏控件元件所引發事件的策略。 您所使用的方法取決於您的應用程式需求。

另請參閱