Zdarzenia podglądu (WPF .NET)

Zdarzenia podglądu, nazywane również zdarzeniami tunelowania, są kierowane zdarzenia przechodzące w dół przez drzewo elementów z 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 na temat tworzenia zdarzenia w wersji zapoznawczej, zobacz Jak utworzyć niestandardowe zdarzenie kierowane.

Ważne

Dokumentacja przewodnika dla komputerów dla platform .NET 7 i .NET 6 jest w budowie.

Wymagania wstępne

W tym artykule przyjęto założenie, że masz podstawową wiedzę na temat zdarzeń kierowanych i zapoznasz się z omówieniem 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 obsługiwane

Należy zachować ostrożność podczas oznaczania zdarzeń podglądu, które są obsługiwane w danych zdarzeń. Oznaczanie zdarzenia podglądu jako obsługiwanego dla elementu innego niż element, który go podniósł, może uniemożliwić element, który go podniósł z obsługi 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 odpowiedniki podglądu i innej niż wersja zapoznawcza (bubbling) poszczególnych zdarzeń. 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 mają być wywoływane, nawet jeśli zdarzenie jest oznaczone jako obsługiwane, ta konfiguracja programu obsługi nie jest powszechna. Aby uzyskać więcej informacji na temat obsługi klas i sposobu jej powiązania ze zdarzeniami w wersji zapoznawczej, zobacz Oznaczanie zdarzeń kierowanych jako obsługiwanych i obsługi klas.

Uwaga

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

Obejście pomijania 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 MouseLeftButtonDown zdarzenie wejściowe bubbling jako obsługiwane w jego OnMouseLeftButtonDown metodzie i zgłasza Click zdarzenie. 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 w katalogu głównym aplikacji obsługiwały zdarzenie kierowane oznaczone jako obsługiwane, możesz wykonać następujące czynności:

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

  • Jeśli zdarzenie oznaczone jako obsługiwane jest zdarzenie bubbling, dołącz programy obsługi dla równoważnego zdarzenia podglądu, jeśli jest dostępne. Jeśli na przykład kontrolka pomija MouseLeftButtonDown zdarzenie, możesz dołączyć procedurę obsługi dla PreviewMouseLeftButtonDown zdarzenia. Takie podejście działa tylko w przypadku zdarzeń wejściowych elementu podstawowego, które implementują strategie routingu tunelowania i rozbijania oraz udostępniają dane zdarzeń.

Poniższy przykład implementuje podstawową kontrolkę niestandardową TextBoxo nazwie componentWrapper , która zawiera element . Kontrolka jest dodawana do StackPanel nazwy 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 klawiszy. W tym wystąpieniu kontrolka componentWrapper :

  1. Oznacza rozsyłane KeyDown zdarzenie kierowane zgodnie z procedurą obsługi, aby je pominąć. W związku z tym wyzwalana jest tylko outerStackPanel procedura obsługi skonfigurowana w kodzie w celu reagowania na obsługiwaneKeyDown zdarzenia. Procedura outerStackPanel obsługi dołączona w języku XAML dla KeyDown zdarzeń nie jest wywoływana.

  2. Wywołuje niestandardowe zdarzenie rozsyłane na trasie o nazwie CustomKey, które wyzwala procedurę outerStackPanelCustomKey obsługi dla zdarzenia.

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 kierowane w wersji zapoznawczej poprzedza równoważne zdarzenie PreviewKeyDown rozsyłane, procedura obsługi w przykładzie jest uruchamiana przed KeyDown procedurą obsługi, która pomija zdarzenia podglądu i bubbling za pośrednictwem udostępnionych danych zdarzenia.

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

Uwaga

Oznaczanie podglądu lub innych niż podgląd odpowiedników zdarzeń wejściowych, które są obsługiwane, to obie strategie pomijania zdarzeń zgłaszanych przez składniki kontrolki. Używane podejście zależy od wymagań aplikacji.

Zobacz też