Freigeben über


Vorschau von Ereignissen

Vorschauereignisse, auch als Tunnelereignisse bezeichnet, sind Routingereignisse, die von dem Anwendungsstammelement zum Element, das das Ereignis ausgelöst hat, abwärts durch die Elementstruktur durchlaufen. Das Element, das ein Ereignis auslöst, wird als Source in den Ereignisdaten gemeldet. Nicht alle Ereignisszenarien unterstützen oder erfordern Vorschauereignisse. In diesem Artikel wird beschrieben, wo Vorschauereignisse vorhanden sind und wie Anwendungen oder Komponenten mit ihnen interagieren können. Informationen zum Erstellen eines Vorschauereignisses finden Sie unter Erstellen eines benutzerdefinierten Routingereignisses.

Voraussetzungen

Im Artikel wird davon ausgegangen, dass Sie über grundlegende Kenntnisse im Zusammenhang mit Routingereignissen verfügen und die Übersicht über Routingereignisse gelesen haben. Um den Beispielen in diesem Artikel zu folgen, hilft es Ihnen, wenn Sie mit Extensible Application Markup Language (XAML) vertraut sind und wissen, wie Sie Windows Presentation Foundation (WPF)-Anwendungen schreiben.

Ereignisse in der Vorschau, die als behandelt markiert wurden

Seien Sie vorsichtig, wenn Sie Vorschauereignisse als in Ereignisdaten behandelt markieren. Das Markieren eines Vorschauereignisses als behandelt für ein anderes Element als das ausgelöste Element kann verhindern, dass das Element, das es ausgelöst hat, das Ereignis behandelt. In manchen Fällen ist das Markieren von Vorschauereignissen als behandelt beabsichtigt. Beispielsweise kann ein zusammengesetztes Steuerelement Ereignisse unterdrücken, die von einzelnen Komponenten ausgelöst werden, und sie durch Ereignisse ersetzen, die vom vollständigen Steuerelement ausgelöst werden. Benutzerdefinierte Ereignisse für ein Steuerelement können angepasste Ereignisdaten bereitstellen und basierend auf Komponentenstatusbeziehungen auslösen.

Bei Eingabeereignissen werden die Ereignisdaten sowohl von den Vorschau- als auch den Nicht-Vorschauversionen (Bubbling) der einzelnen Ereignisse gemeinsam genutzt. Wenn Sie einen Vorschauereignisklassenhandler verwenden, um ein Eingabeereignis als behandelt zu markieren, werden Klassenhandler für das Bubbling-Eingabeereignis in der Regel nicht aufgerufen. Oder wenn Sie einen Vorschau-Instanzhandler verwenden, um ein Ereignis als behandelt zu markieren, werden Instanzhandler für das bubbelnde Eingabeereignis in der Regel nicht aufgerufen. Obwohl Sie Klassen- und Instanzhandler so konfigurieren können, dass sie aufgerufen werden, auch wenn ein Ereignis als behandelt gekennzeichnet ist, ist diese Handlerkonfiguration nicht üblich. Weitere Informationen zur Klassenverarbeitung und ihre Beziehung zu Vorschauereignissen finden Sie unter Markieren von Routingereignissen als behandelt und Klassenverarbeitung.

Hinweis

Nicht alle Vorschauereignisse sind Tunnel-Ereignisse. Beispielsweise folgt das PreviewMouseLeftButtonDown Eingabeereignis einer Abwärtsroute durch den Elementbaum, ist aber ein direktes geroutetes Ereignis, das von jedem UIElement in der Route ausgelöst und neu ausgelöst wird.

Umgang mit der Unterdrückung von Ereignissen durch Steuerelemente

Einige zusammengesetzte Steuerelemente unterdrücken Eingabeereignisse auf Komponentenebene, um sie durch ein angepasstes Ereignis auf hoher Ebene zu ersetzen. Beispielsweise markiert WPF ButtonBase das MouseLeftButtonDown Bubbling-Eingabeereignis als behandelt in der OnMouseLeftButtonDown Methode und löst das Click Ereignis aus. Das MouseLeftButtonDown Ereignis und die zugehörigen Ereignisdaten werden weiterhin entlang der Elementstrukturroute fortgesetzt. Da das Ereignis jedoch als Handled Ereignisdaten gekennzeichnet ist, werden nur Handler aufgerufen, die für die Reaktion auf behandelte Ereignisse konfiguriert sind.

Wenn Sie möchten, dass andere Elemente näher am Hauptelement Ihrer Anwendung ein geroutetes Ereignis behandeln, das als behandelt gekennzeichnet ist, können Sie eine der folgenden Aktionen ausführen:

  • Fügen Sie Handler an, indem Sie die UIElement.AddHandler(RoutedEvent, Delegate, Boolean) Methode aufrufen und den Parameter handledEventsToo auf truefestlegen. Bei diesem Ansatz muss der Ereignishandler im CodeBehind angefügt werden, nachdem ein Objektverweis auf das Element, an das es angefügt wird, bezogen wurde.

  • Wenn das als behandelt markierte Ereignis ein Bubbling-Ereignis ist, fügen Sie Handler für das entsprechende Vorschauereignis an, falls verfügbar. Wenn beispielsweise ein Steuerelement das MouseLeftButtonDown Ereignis unterdrückt, können Sie stattdessen einen Handler für das PreviewMouseLeftButtonDown Ereignis anfügen. Dieser Ansatz funktioniert nur für Basiselementeingabeereignisse, die Sowohl Tunneling- als auch Bubbling-Routingstrategien implementieren und Ereignisdaten freigeben.

Im folgenden Beispiel wird ein rudimentäres benutzerdefiniertes Steuerelement componentWrapper implementiert, das ein TextBox enthält. Das Steuerelement wird einem StackPanel namens outerStackPanel hinzugefügt.

<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>

Das componentWrapper Steuerelement lauscht auf das Bubbling-Ereignis, das KeyDown von der TextBox Komponente ausgelöst wird, wenn ein Tastenanschlag auftritt. Bei dieser Gelegenheit der componentWrapper Kontrolle:

  1. Markiert das KeyDown Bubbling-Routingereignis als behandelt, um es zu unterdrücken. Daher wird nur der Handler ausgelöst, der in Code-Behind konfiguriert ist, um auf outerStackPanel Ereignisse zu reagieren. Der outerStackPanel in XAML für KeyDown Ereignisse angefügte Handler wird nicht aufgerufen.

  2. Löst ein benutzerdefiniertes Routingereignis namens CustomKey aus, das den outerStackPanel Handler für das CustomKey Ereignis aktiviert.

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

Das Beispiel veranschaulicht zwei Problemumgehungen, damit das unterdrückte KeyDown Routingereignis einen Ereignishandler aufruft, der an das folgende outerStackPanel angefügt ist:

  • Befestigen Sie einen PreviewKeyDown-Ereignishandler an outerStackPanel. Da ein Vorschaueingaberoutenereignis dem entsprechenden Bubbling-Routingereignis vorausgeht, wird der PreviewKeyDown-Ereignishandler im Beispiel vor dem KeyDown-Ereignishandler ausgeführt, der sowohl Vorschau- als auch Bubblingereignisse über die gemeinsamen Ereignisdaten unterdrückt.

  • Fügen Sie einen KeyDown-Ereignishandler mithilfe der outerStackPanel-Methode im CodeBehind hinzu, wobei der UIElement.AddHandler(RoutedEvent, Delegate, Boolean)-Parameter auf handledEventsToo festgelegt ist.

Hinweis

Das Markieren von Vorschau- oder Nichtvorschauentsprechungen von Eingabeereignissen wie behandelt sind beide Strategien zum Unterdrücken von Ereignissen, die von den Komponenten eines Steuerelements ausgelöst werden. Der von Ihnen verwendete Ansatz hängt von ihren Anwendungsanforderungen ab.

Siehe auch