Podgląd zdarzeń

Zdarzenia podglądu, nazywane również zdarzeniami tunelowania, to są zdarzenia routowane przechodzące w dół przez drzewo elementów, od elementu głównego aplikacji do elementu, który zgłosił zdarzenie. Element, który zgłasza zdarzenie, jest zgłaszany jako Source w danych zdarzenia. Nie wszystkie scenariusze zdarzeń obsługują lub wymagają zdarzeń w wersji zapoznawczej. W tym artykule opisano, gdzie istnieją zdarzenia w wersji zapoznawczej oraz jak aplikacje lub składniki mogą z nimi współdziałać. Aby uzyskać informacje o tym, jak utworzyć zdarzenie podglądowe, zobacz Jak utworzyć niestandardowe zdarzenie kierowane.

Wymagania wstępne

Artykuł zakłada podstawową znajomość zdarzeń trasowanych i że przeczytałeś Przegląd zdarzeń trasowanych. Aby postępować zgodnie z przykładami w tym artykule, warto zapoznać się z językiem Extensible Application Markup Language (XAML) i wiedzieć, jak pisać aplikacje programu Windows Presentation Foundation (WPF).

Podgląd zdarzeń oznaczonych jako przetwarzane

Należy zachować ostrożność podczas oznaczania zdarzeń podglądu, które są obsługiwane w danych zdarzeń. Oznaczenie zapowiedzi zdarzenia jako obsługiwanej przez element inny niż ten, który je wywołał, może uniemożliwić temu drugiemu elementowi obsługę zdarzenia. Czasami oznaczanie zdarzeń podglądu jako obsłużonych jest zamierzone. Na przykład kontrolka złożona może pomijać zdarzenia wywoływane przez poszczególne składniki i zastępować je zdarzeniami zgłoszonymi przez pełną kontrolkę. Zdarzenia niestandardowe dla kontrolki mogą udostępniać dostosowane dane zdarzeń i wyzwalacz na podstawie relacji stanu składnika.

W przypadku zdarzeń wejściowych dane zdarzeń są współużytkowane zarówno przez wersję podglądową, jak i przez odpowiadającą jej niepodglądową wersję (bąbelkową). Jeśli używasz procedury obsługi zdarzeń w wersji zapoznawczej do oznaczania zdarzenia wejściowego jako obsługiwanego, programy obsługi klas dla zdarzenia wejściowego bubbling zwykle nie będą wywoływane. Lub jeśli używasz programu obsługi zdarzeń w wersji zapoznawczej do oznaczania zdarzenia jako obsługiwanego, programy obsługi wystąpień dla zdarzenia wejściowego bubbling zwykle nie będą wywoływane. Mimo że można skonfigurować programy obsługi klas i wystąpień, które mogą być wywołane, nawet jeśli zdarzenie jest oznaczone jako obsługiwane, taka konfiguracja nie jest powszechna. Aby uzyskać więcej informacji na temat obsługi klas i jej związku ze zdarzeniami podglądu, zobacz Oznaczanie zdarzeń routowanych jako obsługiwane i obsługa klas.

Uwaga / Notatka

Nie wszystkie zdarzenia w wersji zapoznawczej to zdarzenia tunelowania . Na przykład zdarzenie wejściowe PreviewMouseLeftButtonDown podąża trasą w dół przez drzewo elementów, ale jest zdarzeniem kierowanym bezpośrednio, które jest wywoływane i wzbudzane ponownie przez każdy element UIElement w trasie.

Obejście tłumienia zdarzeń przez kontrolki

Niektóre kontrolki złożone pomijają zdarzenia wejściowe na poziomie składnika, aby zastąpić je niestandardowym zdarzeniem wysokiego poziomu. Na przykład, WPF ButtonBase oznacza zdarzenie wejściowe "bubbling" jako obsłużone w swojej metodzie OnMouseLeftButtonDown i zgłasza zdarzenie Click. Zdarzenie MouseLeftButtonDown i jego dane zdarzenia nadal są kontynuowane wzdłuż trasy drzewa elementów, ale ponieważ zdarzenie jest oznaczone jako Handled w danych zdarzenia, wywoływane są tylko programy obsługi skonfigurowane do reagowania na obsługiwane zdarzenia.

Jeśli chcesz, aby inne elementy bliżej korzenia Twojej aplikacji przejmowały obsługę zdarzeń kierowanych, które są oznaczone jako obsługiwane, możesz wykonać następujące czynności:

  • Dołącz procedury obsługi poprzez wywołanie metody UIElement.AddHandler(RoutedEvent, Delegate, Boolean) i ustawienie parametru handledEventsToo na true. Takie podejście wymaga dołączenia procedury obsługi zdarzeń w kodzie za kulisami, po uzyskaniu odwołania do obiektu elementu, do którego będzie dołączony.

  • Jeśli zdarzenie oznaczone jako obsługiwane jest zdarzeniem propagującym się, dołącz procedury obsługi dla odpowiedniego zdarzenia podglądu, jeśli jest dostępne. Jeśli na przykład kontrolka pomija zdarzenie MouseLeftButtonDown, możesz dołączyć procedurę obsługi dla zdarzenia PreviewMouseLeftButtonDown. Takie podejście działa tylko w przypadku zdarzeń wejściowych elementów podstawowych, które implementują strategie tunelowania i propagacji routingu oraz udostępniają dane zdarzeń.

Poniższy przykład implementuje podstawową kontrolkę niestandardową componentWrapper, która zawiera TextBox. Kontrolka jest dodawana do StackPanel o nazwie 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>

Kontrolka componentWrapper nasłuchuje KeyDown zdarzenia bubbling zgłoszonego przez jego TextBox składnik za każdym razem, gdy wystąpi naciśnięcie. W tym przypadku kontrolka componentWrapper :

  1. Oznacza rozsyłane KeyDown zdarzenie kierowane zgodnie z procedurą obsługi, aby je pominąć. W związku z tym, wyzwalany jest tylko outerStackPanel obsługujący skonfigurowany w kodzie zaplecza do reagowania na obsługiwaneKeyDown zdarzenia. Procedura obsługi zdarzeń outerStackPanel, dołączona w XAML dla zdarzeń KeyDown, nie jest wywoływana.

  2. Wywołuje niestandardowe zdarzenie rozsyłane na trasie o nazwie CustomKey, które uruchamia procedurę obsługi outerStackPanel dla zdarzenia 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

W tym przykładzie przedstawiono dwa obejścia umożliwiające pobranie pominiętego KeyDown zdarzenia kierowanego w celu wywołania procedury obsługi zdarzeń dołączonej do elementu outerStackPanel:

  • Dołącz procedurę obsługi zdarzeń PreviewKeyDown do programu outerStackPanel. Ponieważ zdarzenie wstępnego skierowania wejścia poprzedza równoważne zdarzenie kaskadowe, procedura obsługi w przykładzie jest uruchamiana przed procedurą obsługi tłumiącą zarówno zdarzenia podglądu, jak i kaskadowe za pośrednictwem ich udostępnionych danych zdarzenia.

  • Dołącz procedurę obsługi zdarzeń KeyDown do outerStackPanel przy użyciu metody UIElement.AddHandler(RoutedEvent, Delegate, Boolean) w kodzie za pomocą parametru ustawionego na truehandledEventsToo.

Uwaga / Notatka

Oznaczanie podglądu lub niepodglądowych odpowiedników zdarzeń wejściowych jako obsługiwanych to dwie strategie pomijania zdarzeń zgłaszanych przez składniki kontrolki. Używane podejście zależy od wymagań aplikacji.

Zobacz także