Übersicht über Routingereignisse (WPF .NET)

Entwickler und Ersteller von WPF-Anwendungen (Windows Presentation Foundation) und -Komponenten können Routingereignisse verwenden, um Ereignisse über eine Elementstruktur zu verteilen und Ereignishandler für mehrere Listener in der Struktur aufzurufen. Diese Features werden in CLR-Ereignissen (Common Language Runtime) nicht gefunden. Mehrere WPF-Ereignisse sind Routingereignisse, z. B. ButtonBase.Click. In diesem Artikel werden die grundlegenden Konzepte für Routingereignisse erläutert und es wird erklärt, wann und wie Sie auf Routingereignisse reagieren sollten.

Voraussetzungen

In diesem Artikel werden Grundkenntnisse der Common Language Runtime (CLR) und der objektorientierten Programmierung vorausgesetzt. Außerdem wird erläutert, wie das WPF-Elementlayout als Struktur konzeptualisiert werden kann. Für die Beispiele in diesem Artikel ist es hilfreich, wenn Sie mit Extensible Application Markup Language (XAML) vertraut sind und wissen, wie WPF-Anwendungen geschrieben werden.

Was ist ein Routingereignis?

Sie können Routingereignisse aus einer funktionalen oder einer Implementierungsperspektive betrachten:

  • Aus einer funktionalen Perspektive ist ein Routingereignis ein Ereignistyp, der Handler für mehrere Listener in einer Elementstruktur und nicht nur in der Ereignisquelle aufrufen kann. Ein Ereignislistener ist das Element, an das ein Ereignishandler angefügt und aufgerufen wird. Eine Ereignisquelle ist das Element oder Objekt, das ursprünglich ein Ereignis ausgelöst hat.

  • Aus der Sicht der Implementierung ist ein Routingereignis ein Ereignis, das beim WPF-Ereignissystem registriert ist, von einer Instanz der RoutedEvent-Klasse unterstützt und vom WPF-Ereignissystem verarbeitet wird. In der Regel wird ein Routingereignis mit einem CLR-Ereigniswrapper implementiert, um das Anfügen von Handlern in XAML und CodeBehind wie bei einem CLR-Ereignis zu ermöglichen.

WPF-Anwendungen enthalten in der Regel viele Elemente, die entweder in XAML deklariert oder im Code instanziiert wurden. Die Elemente einer Anwendung sind in der Elementstruktur vorhanden. Folgendes geschieht, wenn das Ereignis für ein Quellelement ausgelöst wird, je nach Definition des Routingereignisses:

  • Bewegt sich in der Elementstruktur vom Quellelement zum Stammelement nach oben, bei dem es sich in der Regel um eine Seite oder ein Fenster handelt.
  • Bewegt sich in der Elementstruktur vom Stammelement zum Quellelement nach unten.
  • Durchläuft nicht die Elementstruktur und tritt nur für das Quellelement auf.

Betrachten Sie die folgende partielle Elementstruktur:

<Border Height="30" Width="200" BorderBrush="Gray" BorderThickness="1">
    <StackPanel Background="LightBlue" Orientation="Horizontal" Button.Click="YesNoCancelButton_Click">
        <Button Name="YesButton">Yes</Button>
        <Button Name="NoButton">No</Button>
        <Button Name="CancelButton">Cancel</Button>
    </StackPanel>
</Border>

Die Elementstruktur wird wie dargestellt gerendert:

Eine XAML-Elementstruktur mit drei Schaltflächen: Ja, Nein und Abbrechen.

Jede der drei Schaltflächen ist eine potenzielle Click-Ereignisquelle. Wenn auf eine der Schaltflächen geklickt wird, löst es das Click-Ereignis aus, das von der Schaltfläche bis zum Stammelement nach oben bewegt wird. Die Elemente Button und Border verfügen nicht über angefügte Ereignishandler, das Element StackPanel jedoch schon. Möglicherweise sind andere Elemente in der Struktur weiter oben, die nicht angezeigt werden, denen aber auch Click-Ereignishandler angefügt sind. Wenn das Click-Ereignis das StackPanel-Element erreicht, ruft das WPF-Ereignissystem den YesNoCancelButton_Click-Handler auf, der daran angefügt ist. Die Ereignisroute für das Click-Ereignis im Beispiel lautet: Button ->StackPanel ->Border -> aufeinander folgende übergeordnete Elemente.

Hinweis

Das Element, das ursprünglich ein Routingereignis ausgelöst hat, wird in den Parametern des Ereignishandlers als RoutedEventArgs.Source identifiziert. Der Ereignislistener ist das Element, an das der Ereignishandler angefügt und aufgerufen wird, und wird in den Parametern des Ereignishandlers als Absender identifiziert.

Szenarios auf oberster Ebene für Routingereignisse

Hier sind einige der Szenarien, die das Konzept der Routingereignisse motiviert haben und die es von einem typischen CLR-Ereignis unterscheiden:

  • Zusammensetzung und Kapselung von Steuerelementen: Verschiedene Steuerelemente in WPF verfügen über ein umfangreiches Inhaltsmodell. Sie können z. B. ein Bild in ein Button-Element einfügen, wodurch die visuelle Struktur der Schaltfläche erweitert wird. Das hinzugefügte Bild darf jedoch nicht das Treffertestverhalten der Schaltfläche beeinträchtigen, die reagieren muss, wenn ein Benutzer auf die Bildpixel klickt.

  • Anfügepunkte für einzelnen Handler: Sie könnten für jedes Click-Ereignis einer Schaltfläche einen Handler registrieren, aber bei Routingereignissen können Sie einen einzelnen Handler anfügen, wie in dem vorherigen XAML-Beispiel gezeigt. Auf diese Weise können Sie die Elementstruktur unter dem einzelnen Handler ändern, z. B. das Hinzufügen oder Entfernen weiterer Schaltflächen, ohne das Click-Ereignis der einzelnen Schaltflächen registrieren zu müssen. Wenn das Click-Ereignis ausgelöst wird, kann die Handlerlogik ermitteln, woher das Ereignis stammt. Der folgende Handler, der in der zuvor gezeigten XAML-Elementstruktur angegeben ist, enthält die folgende Logik:

    private void YesNoCancelButton_Click(object sender, RoutedEventArgs e)
    {
        FrameworkElement sourceFrameworkElement = e.Source as FrameworkElement;
        switch (sourceFrameworkElement.Name)
        {
            case "YesButton":
                // YesButton logic.
                break;
            case "NoButton":
                // NoButton logic.
                break;
            case "CancelButton":
                // CancelButton logic.
                break;
        }
        e.Handled = true;
    }
    
    Private Sub YesNoCancelButton_Click(sender As Object, e As RoutedEventArgs)
        Dim frameworkElementSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
    
        Select Case frameworkElementSource.Name
            Case "YesButton"
                ' YesButton logic.
            Case "NoButton"
                ' NoButton logic.
            Case "CancelButton"
                ' CancelButton logic.
        End Select
    
        e.Handled = True
    End Sub
    
  • Klassenbehandlung: Routingereignisse unterstützen einen Klassenereignishandler, den Sie in einer Klasse definieren. Klassenhandler behandeln ein Ereignis vor allen Instanzhandlern für dasselbe Ereignis für jede Instanz der Klasse.

  • Verweisen auf ein Ereignis ohne Reflexion: Jedes Routingereignis erstellt einen RoutedEvent-Feldbezeichner, um eine robuste Technik zur Identifizierung von Ereignissen bereitzustellen, die keine statischen oder Laufzeitreflexionen zur Identifizierung des Ereignisses erfordert.

Implementieren von Routingereignissen

Ein Routingereignis ist ein Ereignis, das beim WPF-Ereignissystem registriert ist, von einer Instanz der RoutedEvent-Klasse unterstützt und vom WPF-Ereignissystem verarbeitet wird. Die Speicherung der RoutedEvent-Instanz, die über die Registrierung abgerufen wird, erfolgt in der Regel als public static readonly-Mitglied der Klasse, die sie registriert hat. Diese Klasse wird als „owner“-Ereignisklasse (Besitzer) bezeichnet. In der Regel implementiert ein Routingereignis einen identisch benannten CLR-Ereigniswrapper. Der CLR-Ereigniswrapper enthält add- und remove-Zugriffsmethoden, um das Anfügen von Handlern in XAML und CodeBehind über sprachspezifische Ereignissyntax zu ermöglichen. Die Zugriffsmethoden add und remove setzen die CLR-Implementierung außer Kraft und rufen das Routingereignis AddHandler und RemoveHandler-Methoden auf. Der Unterstützungs- und Verbindungsmechanismus des Routingereignisses ist vom Konzept her genauso wie eine Abhängigkeitseigenschaft eine CLR-Eigenschaft ist, die von der DependencyProperty-Klasse unterstützt und im WPF-Eigenschaftensystem registriert wird.

Im folgenden Beispiel wird das Tap-Routingereignis registriert, die zurückgegebene RoutedEvent-Instanz gespeichert und ein CLR-Ereigniswrapper implementiert.

// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    name: "Tap",
    routingStrategy: RoutingStrategy.Bubble,
    handlerType: typeof(RoutedEventHandler),
    ownerType: typeof(CustomButton));

// Provide CLR accessors for adding and removing an event handler.
public event RoutedEventHandler Tap
{
    add { AddHandler(TapEvent, value); }
    remove { RemoveHandler(TapEvent, value); }
}
' Register a custom routed event using the Bubble routing strategy.
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
    name:="Tap",
    routingStrategy:=RoutingStrategy.Bubble,
    handlerType:=GetType(RoutedEventHandler),
    ownerType:=GetType(CustomButton))

' Provide CLR accessors for adding and removing an event handler.
Public Custom Event Tap As RoutedEventHandler
    AddHandler(value As RoutedEventHandler)
        [AddHandler](TapEvent, value)
    End AddHandler

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

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

Routingstrategien

Routingereignisse verwenden eine von drei Routingstrategien:

  • Bubbling: Zunächst werden Ereignishandler für die Ereignisquelle aufgerufen. Das Routingereignis wird dann zu den übergeordneten Elementen weitergeleitet und ruft deren Ereignishandler nacheinander auf, bis es den Stamm der Elementstruktur erreicht. Die meisten Routingereignisse verwenden die Bubblingroutingstrategie. Bubblingroutingereignisse werden üblicherweise verwendet, um Eingabe- oder Zustandsänderungen von zusammengesetzten Steuerelementen oder anderen Elementen der Benutzeroberfläche zu melden.

  • Tunneling: Zunächst werden Ereignishandler am Stamm der Elementstruktur aufgerufen. Das Routingereignis wird dann zu den untergeordneten Elementen weitergeleitet und ruft deren Ereignishandler nacheinander auf, bis es die Ereignisquelle erreicht. Ereignisse, die einer Tunnelingroute folgen, werden auch als Vorschauereignisse bezeichnet. WPF-Eingabeereignisse werden in der Regel als Vorschau- und Bubblingpaare implementiert.

  • Direkt: Es werden nur Ereignishandler für die Ereignisquelle aufgerufen. Diese Nicht-Routingstrategie entspricht Frameworkereignissen der Windows Forms-Benutzeroberfläche, die CLR-Standardereignisse sind. Im Gegensatz zu CLR-Ereignissen unterstützen direkte Routingereignisse Klassenbehandlung und können von EventSetters und EventTriggers verwendet werden.

Was ist der Vorteil von Routingereignissen?

Als Anwendungsentwickler müssen Sie nicht immer wissen oder sicherstellen, dass das Ereignis, das Sie behandeln, als Routingereignis implementiert wird. Weitergeleitete Ereignisse weisen ein besonderes Verhalten auf, aber dieses Verhalten ist weitestgehend unsichtbar, wenn Sie ein Ereignisses auf dem Element behandeln, von dem es ausgelöst wurde. Routingereignisse sind jedoch relevant, wenn Sie einen Ereignishandler an ein übergeordnetes Element anfügen möchten, um Ereignisse zu behandeln, die von untergeordneten Elementen ausgelöst werden, z. B. innerhalb eines zusammengesetzten Steuerelements.

Für Routingereignislistener ist es nicht erforderlich, dass die von ihnen behandelten Routingereignisse Mitglieder ihrer Klasse sind. Jedes UIElement oder ContentElement kann ein Ereignislistener für jedes weitergeleitete Ereignis sein. Da sich visuelle Elemente von UIElement oder ContentElement ableiten, können Sie Routingereignisse als konzeptionelle „Schnittstelle“ verwenden, die den Austausch von Ereignisinformationen zwischen verschiedenen Elementen in einer Anwendung unterstützt. Das „Schnittstellen“-Konzept für Routingereignisse kann besonders auf Eingabeereignisse angewendet werden.

Routingereignisse unterstützen den Austausch von Ereignisinformationen zwischen Elementen entlang der Ereignisroute, da jeder Listener Zugriff auf dieselbe Instanz von Ereignisdaten hat. Wenn ein Element etwas in den Ereignisdaten ändert, ist diese Änderung für nachfolgende Elemente in der Ereignisroute sichtbar.

Abgesehen vom Routingaspekt könnten Sie sich aus diesen Gründen für die Implementierung eines Routingereignisses anstelle eines CLR-Standardereignisses entscheiden:

  • Einige Features zur WPF-Format- und -Vorlagenerstellung wie EventSetters und EventTriggers erfordern, dass das referenzierte Ereignis ein Routingereignis ist.

  • Routingereignisse unterstützen Klassenereignishandler, die ein Ereignis vor allen Instanzhandlern für dasselbe Ereignis für jede Instanz der Listenerklasse behandeln. Dieses Feature ist beim Entwurf von Steuerelementen nützlich, da Ihr Ereignishandler ereignisgesteuertes Klassenverhalten erzwingen kann, das nicht versehentlich durch einen Instanzhandler unterdrückt werden kann.

Anfügen und Implementieren eines Routingereignishandlers

In XAML fügen Sie einen Ereignishandler an ein Element an, indem Sie den Ereignisnamen als Attribut für das Ereignislistenerelement deklarieren. Der Attributwert ist der Name der Handlermethode. Die Handlermethode muss in der partiellen CodeBehind-Klasse für die XAML-Seite implementiert werden. Der Ereignislistener ist das Element, an das der Ereignishandler angefügt und aufgerufen wird.

Für ein Ereignis, das Mitglied (geerbt oder anderweitig) der Listenerklasse ist, können Sie einen Handler wie folgt anfügen:

<Button Name="Button1" Click="Button_Click">Click me</Button>

Wenn das Ereignis kein Mitglied der Listenerklasse ist, müssen Sie den qualifizierten Ereignisnamen in Form von <owner type>.<event name> verwenden. Da die StackPanel-Klasse z. B. das Click-Ereignis nicht implementiert, müssen Sie die Syntax für den qualifizierten Ereignisnamen verwenden, um einen Handler an eine StackPanel für ein Click-Ereignis anzufügen, das sich nach oben zu diesem Element bewegt:

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

Die Signatur der Ereignishandlermethode in CodeBehind muss mit dem Delegattyp für das Routingereignis übereinstimmen. Der sender-Parameter des RoutedEventHandler-Delegaten für das Click-Ereignis gibt das Element an, an das der Ereignishandler angefügt ist. Der args-Parameter des RoutedEventHandler-Delegaten enthält die Ereignisdaten. Eine kompatible CodeBehind-Implementierung für den Button_Click-Ereignishandler kann wie folgt lauten:

private void Button_Click(object sender, RoutedEventArgs e)
{
    // Click event logic.
}
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    ' Click event logic.
End Sub

Obwohl RoutedEventHandler der grundlegende Handlerdelegat für das Routingereignis ist, erfordern einige Steuerelemente oder Implementierungsszenarien unterschiedliche Delegaten, die spezialisiertere Ereignisdaten unterstützen. Beispielsweise sollte der Handler für das DragEnter-Routingereignis den DragEventHandler-Delegaten implementieren. Dadurch kann ihr Handlercode auf die DragEventArgs.Data-Eigenschaft in Ereignisdaten zugreifen, die die Nutzdaten der Zwischenablage aus dem Ziehvorgang enthält.

Die XAML-Syntax zum Hinzufügen von Routingereignishandlern ist identisch mit den CLR-Standardereignishandlern. Weitere Informationen zum Hinzufügen von Ereignishandlern in XAML finden Sie unter Übersicht über XAML (WPF). Ein vollständiges Beispiel zum Anfügen eines Ereignishandlers an ein Element mit XAML finden Sie unter Behandeln eines Routingereignisses.

Zum Anfügen eines Ereignishandlers für ein Routingereignis an ein Element mithilfe von Code haben Sie im Allgemeinen zwei Optionen:

  • Rufen Sie die AddHandler-Methode direkt auf. Routingereignishandler können immer auf diese Weise angefügt werden. In diesem Beispiel wird ein Click-Ereignishandler mithilfe der AddHandler-Methode an eine Schaltfläche angefügt:

    Button1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    Button1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    

    So fügen Sie einen Handler für das Click-Ereignis der Schaltfläche an ein anderes Element in der Route des Ereignisses an, z. B. ein StackPanel namens StackPanel1:

    StackPanel1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    StackPanel1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    
  • Wenn das Routingereignis einen CLR-Ereigniswrapper implementiert, verwenden Sie sprachspezifische Ereignissyntax, um Ereignishandler wie für ein standardmäßiges CLR-Ereignis hinzuzufügen. Die meisten vorhandenen WPF-Routingereignisse implementieren den CLR-Wrapper, wodurch sprachspezifische Ereignissyntax aktiviert wird. In diesem Beispiel wird ein Click-Ereignishandler mithilfe einer sprachspezifischen Syntax an eine Schaltfläche angefügt:

    Button1.Click += Button_Click;
    
    AddHandler Button1.Click, AddressOf Button_Click
    

Ein Beispiel für das Anfügen eines Ereignishandlers in Code finden Sie unter Hinzufügen eines Ereignishandlers mithilfe von Code. Wenn Sie Visual Basic für die Codierung verwenden, können Sie auch das Schlüsselwort Handles verwenden, um Handler als Teil der Handlerdeklarationen hinzufügen. Weitere Informationen finden Sie unter Visual Basic- und WPF-Ereignisbehandlung.

Das Handled-Konzept

Alle Routingereignisse haben eine gemeinsame Basisklasse für Ereignisdaten, nämlich die RoutedEventArgs-Klasse. Die RoutedEventArgs-Klasse definiert die boolesche Handled-Eigenschaft. Der Zweck der Handled-Eigenschaft besteht darin, jedem Ereignishandler entlang der Ereignisroute das Markieren des Routingereignisses als behandelt (handled) zu ermöglichen. Um ein Ereignis als behandelt zu markieren, legen Sie den Wert von Handled im Ereignishandlercode auf true fest.

Der Wert Handled wirkt sich darauf aus, wie ein Routingereignis auf seinem Weg entlang der Ereignisroute verarbeitet wird. Wenn Handled in den freigegebenen Ereignisdaten eines Routingereignisses entsprechend true ist, werden Handler, die an andere Elemente weiter entlang der Ereignisroute angefügt sind, normalerweise nicht für diese bestimmte Ereignisinstanz aufgerufen. Bei den meisten gängigen Handlerszenarien verhindert das Markieren eines Ereignisses als „behandelt“, dass nachfolgende Handler entlang der Ereignisroute, egal ob Instanz- oder Klassenhandler, auf dieses bestimmte Ereignis reagieren. In den seltenen Fällen, in denen Ihr Ereignishandler auf Routingereignisse reagieren muss, die als behandelt markiert wurden, können Sie jedoch wie folgt vorgehen:

Das Konzept von Handled kann sich darauf auswirken, wie Sie Ihre Anwendung entwerfen und Ihre Ereignishandler codieren. Sie können Handled als einfaches Protokoll für die Verarbeitung von Routingereignissen konzeptualisieren. Wie Sie dieses Protokoll verwenden, liegt bei Ihnen, aber die erwartete Verwendung des Handled-Parameters lautet:

  • Wenn ein Routingereignis als behandelt markiert ist, muss es nicht von anderen Elementen auf der Route erneut verarbeitet werden.

  • Wenn ein Routingereignis nicht als behandelt markiert ist, verfügen Listener weiter oben in der Ereignisroute nicht über einen Handler für das Ereignis, oder keiner der registrierten Handler hat auf das Ereignis in einer Weise reagiert, dass es gerechtfertigt ist, das Ereignis als behandelt zu markieren. Handler für den aktuellen Listener verfügen über drei mögliche Vorgehensweisen:

    • Keine Aktion durchführen. Das Ereignis bleibt unbehandelt und wird an den nächsten Listener in der Struktur weitergeleitet.

    • Code als Reaktion auf das Ereignis ausführen, aber nicht in einem Ausmaß, das es rechtfertigt, das Ereignis als behandelt zu markieren. Das Ereignis bleibt unbehandelt und wird an den nächsten Listener in der Struktur weitergeleitet.

    • Code als Reaktion auf das Ereignis zu einem Ausmaß ausführen, das es rechtfertigt, das Ereignis als behandelt zu markieren. Das Ereignis in den Ereignisdaten als behandelt markieren. Das Ereignis wird immer noch an den nächsten Listener in der Struktur weitergeleitet, aber die meisten Listener rufen keine weiteren Handler auf. Die Ausnahme sind Listener mit Handlern, die speziell mit handledEventsToo registriert wurden, das auf true festgelegt wurde.

Weitere Informationen zur Behandlung von Routingereignissen finden Sie unter Markieren von Routingereignissen als behandelt und Klassenbehandlung.

Auch wenn Entwickler, die ein Bubblingroutingereignis nur für das Objekt behandeln, das es ausgelöst hat, sich nicht um andere Listener kümmern müssen, ist es eine bewährte Methode, das Ereignis trotzdem als behandelt zu markieren. Dadurch werden unvorhergesehene Nebeneffekte vermieden, wenn ein Element, das weiter hinten auf der Ereignisroute liegt, einen Handler für das gleiche Routingereignis aufweist.

Klassenhandler

Bei Routingereignishandlern kann es sich um Instanzhandler oder Klassenhandler handeln. Klassenhandler für eine bestimmte Klasse werden vor jedem Instanzhandler aufgerufen, der auf dasselbe Ereignis in einer Instanz dieser Klasse reagiert. Wenn Routingereignisse als behandelt markiert werden, erfolgt diese Markierung aufgrund dieses Verhaltens häufig innerhalb von Klassenhandlern. Es gibt zwei Typen von Klassenhandlern:

  • Statische Klassenereignishandler, die durch Aufrufen der RegisterClassHandler-Methode innerhalb eines statischen Klassenkonstruktors registriert werden.
  • Überschreibende Klassenereignishandler, die durch Überschreiben (Außerkraftsetzen) der virtuellen Ereignismethoden der Basisklasse registriert werden. Virtuelle Ereignismethoden der Basisklasse sind in erster Linie für Eingabeereignisse vorhanden und weisen Namen auf, die mit On<Ereignisname> und OnPreview<Ereignisname> beginnen.

Einige WPF-Steuerelemente verfügen über inhärente Klassenbehandlung für bestimmte Routingereignisse. Die Klassenbehandlung mag den Anschein erwecken, dass das Routingereignis nie ausgelöst wird, aber in Wirklichkeit wird es von einem Klassenhandler als behandelt markiert. Wenn Sie möchten, dass Ihr Ereignishandler auf das behandelte Ereignis reagiert, können Sie Ihren Handler mit handledEventsToo registrieren, das auf true festgelegt ist. Weitere Informationen zum Implementieren eigener Klassenhandler oder zum Umgehen unerwünschter Klassenbehandlungen finden Sie unter Markieren von Routingereignissen als behandelt und Klassenbehandlung.

Angefügte Ereignisse in WPF

Die XAML-Sprache definiert auch eine besondere Art von Ereignissen, die als angefügte Ereignisse bezeichnet werden. Angefügte Ereignisse können verwendet werden, um ein neues Routingereignis in einer Nicht-Elementklasse zu definieren und dieses Ereignis für jedes Element in der Struktur auszulösen. Dazu müssen Sie das angefügte Ereignis als Routingereignis registrieren und spezifischen unterstützenden Code bereitstellen, die angefügte Ereignisfunktionen unterstützen. Da angefügte Ereignisse als Routingereignisse registriert werden, wenn sie für ein Element ausgelöst werden, erfolgt die Verteilung über die Elementstruktur.

In der XAML-Syntax wird ein angefügtes Ereignis durch den Ereignisnamen und Besitzertyp in Form von <owner type>.<event name> angegeben. Da der Ereignisname mit dem Namen des Besitzertyps qualifiziert ist, ermöglicht die Syntax, dass das Ereignis an jedes Element angefügt werden kann, für das eine Instanziierung möglich ist. Diese Syntax ist auch auf Handler für reguläre Routingereignisse anwendbar, die an ein beliebiges Element entlang der Ereignisroute angefügt werden. Sie können auch Handler für angefügte Ereignisse in CodeBehind anfügen, indem Sie die AddHandler-Methode für das Objekt aufrufen, an das der Handler angefügt werden soll.

Das WPF-Eingabesystem verwendet angefügte Ereignisse in großem Umfang. Allerdings werden fast alle dieser angefügten Ereignisse als äquivalente nicht angefügte Routingereignisse über Basiselemente angezeigt. Sie werden angefügte Ereignisse nur selten direkt verwenden oder behandeln. So ist es beispielsweise einfacher, das zugrunde liegende angefügte Mouse.MouseDown-Ereignis für ein UIElement über das entsprechende UIElement.MouseDown-Routingereignis zu behandeln als mithilfe der angefügten Ereignissyntax in XAML oder CodeBehind.

Weitere Informationen zu angefügten Ereignissen in WPF finden Sie unter Übersicht über angefügte Ereignisse.

Qualifizierte Ereignisnamen in XAML

Die <owner type>.<event name>-Syntax qualifiziert einen Ereignisnamen mit dem Namen seines Besitzertyps. Mit dieser Syntax kann ein Ereignis an jedes Element angefügt werden, nicht nur an Elemente, die das Ereignis als Mitglied ihrer Klasse implementieren. Die Syntax gilt beim Anfügen von Handlern in XAML für angefügte Ereignisse oder Routingereignisse für beliebige Elemente entlang der Ereignisroute. Stellen Sie sich vor, Sie möchten einen Handler an ein übergeordnetes Element anfügen, um Routingereignisse zu behandeln, die für untergeordnete Elemente ausgelöst wurden. Wenn das Routingereignis kein Mitglied des übergeordneten Elements ist, müssen Sie die Syntax für qualifizierte Ereignisnamen verwenden. Beispiel:

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

Im Beispiel ist der übergeordnete Elementlistener, dem der Ereignishandler hinzugefügt wird, ein StackPanel. Das Click-Routingereignis wird jedoch für die ButtonBase-Klasse implementiert und ausgelöst und steht der Button-Klasse durch Vererbung zur Verfügung. Obwohl die Button-Klasse das Click-Ereignis „besitzt“, lässt das Routingereignissystem Handler für jedes Routingereignis zu, das an einen beliebigen UIElement- oder ContentElement-Instanzlistener angefügt werden kann, der andernfalls Handler für ein CLR-Ereignis aufweisen könnte. Der standardmäßige xmlns-Namespace für diese qualifizierten Attributnamen ist in der Regel der Standard-WPF-xmlns-Namespace, aber Sie können auch Namespaces mit Präfix für benutzerdefinierte Routingereignisse angeben. Weitere Informationen zu xmlns finden Sie unter XAML-Namespaces und Namespacezuordnung für WPF-XAML.

Eingabeereignisse in WPF

Routingereignisse werden auf der WPF-Plattform häufig für Eingabeereignisse verwendet. Standardmäßig weisen WPF-Routingereignisse, die einer Tunnelingroute folgen, einen Namen auf, der mit dem Präfix „Preview“ (Vorschau) versehen ist. Das Präfix „Preview“ gibt an, dass das Vorschauereignis abgeschlossen wird, bevor das gekoppelte Bubblingereignis gestartet wird. Eingabeereignisse treten oft paarweise auf, wobei das eine ein Vorschauereignis und das andere ein Bubblingroutingereignis ist. Beispiel: PreviewKeyDown und KeyDown. Die Ereignispaare verwenden dieselbe Instanz von Ereignisdaten, die für PreviewKeyDown und KeyDown vom Typ KeyEventArgs ist. Manchmal haben Eingabeereignisse nur eine Bubblingversion oder nur eine direkte Routingversion. In der API-Dokumentation verweisen die Themen für Routingereignisse auf Paare von Routingereignissen und erläutern die Routingstrategie für jedes Routingereignis.

WPF-Eingabeereignisse, die paarweise auftreten, sind so implementiert, dass eine einzelne Benutzeraktion von einem Eingabegerät, z. B. das Drücken einer Maustaste, die Vorschau- und die Bubblingroutingereignisse nacheinander auslöst. Zunächst wird das Vorschauereignis ausgelöst und seine Route abgeschlossen. Nach Abschluss des Vorschauereignisses wird das Bubblingereignis ausgelöst und seine Route abgeschlossen. Der RaiseEvent-Methodenaufruf in der implementierenden Klasse, die das Bubblingereignis auslöst, verwendet die Ereignisdaten aus dem Vorschauereignis für das Bubblingereignis wieder.

Ein als behandelt markiertes Vorschaueingabeereignis ruft keine normal registrierten Ereignishandler für den Rest der Vorschauroute auf, und das gekoppelte Bubblingereignis wird nicht ausgelöst. Dieses Verhalten ist nützlich für Designer von zusammengesetzten Steuerelementen, die auf Treffertests basierende Eingabeereignisse oder fokusbasierte Eingabeereignisse auf der obersten Ebene ihres Steuerelements gemeldet haben möchten. Elemente der obersten Ebene des Steuerelements haben die Möglichkeit, Vorschauereignisse von Steuerelementunterkomponenten zu behandeln, um sie durch ein steuerelementspezifisches Ereignis auf oberster Ebene zu „ersetzen“.

Betrachten Sie zur Veranschaulichung, wie die Verarbeitung von Eingabeereignissen funktioniert, das folgende Beispiel eines Eingabeereignisses. In der folgenden Strukturdarstellung ist leaf element #2 die Quelle der gekoppelten Ereignisse PreviewMouseDown und MouseDown:

Ein Diagramm, das zeigt, wie das Ereignisrouting von einem Stammelement zu anderen Elementen fließt.

Die Reihenfolge der Ereignisverarbeitung nach einer Abwärtsaktion der Maus auf dem Blattelement Nr. 2 ist:

  1. PreviewMouseDown-Tunnelingereignis für das Stammelement.
  2. PreviewMouseDown-Tunnelingereignis für Zwischenelement Nr. 1.
  3. PreviewMouseDown-Tunnelingereignis für Blattelement Nr. 2, bei dem es sich um das Quellelement handelt.
  4. MouseDown-Bubblingereignis für Blattelement Nr. 2, bei dem es sich um das Quellelement handelt.
  5. MouseDown-Bubblingereignis für Zwischenelement Nr. 1.
  6. MouseDown-Bubblingereignis für das Stammelement.

Ein Delegat eines Routingereignishandlers enthält Verweise sowohl auf das Objekt, das das Ereignis ausgelöst hat, als auch auf das Objekt, auf dem der Handler aufgerufen wurde. Das Objekt, das das Ereignis ursprünglich ausgelöst hat, wird von der Source-Eigenschaft in den Ereignisdaten gemeldet. Das Objekt, in dem der Handler aufgerufen wurde, wird vom Sender-Parameter gemeldet. Bei einer bestimmten Routingereignisinstanz ändert sich das Ereignis auslösende Objekt nicht, wenn das Ereignis die Elementstruktur durchläuft, wohl aber die sender-Instanz selbst. In den Schritten 3 und 4 des vorherigen Diagramms sind Source und sender dasselbe Objekt.

Wenn der Eingabeereignishandler die anwendungsspezifische Logik abschließt, die zum Adressieren des Ereignisses erforderlich ist, sollten Sie das Eingabeereignis als behandelt markieren. Sobald ein Eingabeereignis als Handledmarkiert ist, werden Handler entlang der Ereignisroute nicht aufgerufen. Ereignishandler für Eingaben, die mit dem auf true festgelegten Parameter handledEventsToo registriert sind, werden jedoch auch dann aufgerufen, wenn das Ereignis als behandelt markiert ist. Weitere Informationen finden Sie unter Vorschauereignisse und Markieren von Routingereignissen als behandelt und Klassenbehandlung.

Das Konzept der Vorschau- und Bubblingereignispaare mit freigegebenen Ereignisdaten und sequenziellem Auslösen des Vorschau- und Bubblingereignisses gilt nur für einige WPF-Eingabeereignisse und nicht für alle Routingereignisse. Wenn Sie Ihr eigenes Eingabeereignis implementieren, um ein erweitertes Szenario zu behandeln, sollten Sie den Ansatz der WPF-Eingabeereignispaare verfolgen.

Wenn Sie Ihr eigenes zusammengesetztes Steuerelement implementieren, das auf Eingabeereignisse reagiert, sollten Sie die Verwendung von Vorschauereignissen in Erwägung ziehen, um Eingabeereignisse, die für Unterkomponenten ausgelöst werden, zu unterdrücken und durch ein Ereignis auf oberster Ebene zu ersetzen, das das gesamte Steuerelement darstellt. Weitere Informationen finden Sie unter Markieren von Routingereignissen als behandelt und Klassenbehandlung.

Weitere Informationen zum WPF-Eingabesystem und zur Interaktion von Eingaben und Ereignissen in typischen Anwendungsszenarien finden Sie unter Übersicht über die Eingabe.

EventSetters und EventTriggers

In Markupformaten können Sie eine vordefinierte XAML-Ereignisbehandlungssyntax mithilfe einer EventSetter-Instanz einschließen. Bei der XAML-Verarbeitung wird der referenzierte Handler zur formatierten Instanz hinzugefügt. Sie können nur eine EventSetter-Instanz für ein Routingereignis deklarieren. Im folgenden Beispiel wird die referenzierte ApplyButtonStyle-Ereignishandlermethode in CodeBehind implementiert.

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type Button}">
            <EventSetter Event="Click" Handler="ApplyButtonStyle"/>
        </Style>
    </StackPanel.Resources>
    <Button>Click me</Button>
    <Button Click="Button_Click">Click me</Button>
</StackPanel>

Wahrscheinlich enthält der Knoten Style bereits andere Stilinformationen, die sich auf Steuerelemente des angegebenen Typs beziehen, und dass die EventSetter-Instanz Teil dieser Stile sein, fördert die Wiederverwendung von Code sogar auf Markupebene. Darüber hinaus abstrahiert EventSetter Methodennamen für Handler, die sich von der allgemeinen Anwendung und dem Seitenmarkup entfernt befinden.

EventTrigger ist eine weitere spezialisierte Syntax, die das Routingereignis mit Animationsfunktionen von WPF kombiniert. Wie bei der EventSetter-Instanz können Sie nur eine EventTrigger-Instanz für ein Routingereignis deklarieren. Normalerweise wird EventTrigger als Teil eines Stils deklariert, aber EventTrigger kann auf Seitenebene als Teil der Triggers-Sammlung oder in einer ControlTemplate deklariert werden. Ein EventTrigger ermöglicht es Ihnen, ein Storyboard anzugeben, das immer dann ausgeführt wird, wenn ein weitergeleitetes Ereignis ein Element in seiner Route erreicht, das ein EventTrigger für dieses Ereignis deklariert. Der Vorteil von EventTrigger gegenüber der einfachen Verarbeitung des Ereignisses und dem Starten eines vorhandenen Storyboards ist, dass EventTrigger eine bessere Kontrolle über das Storyboard und sein Laufzeitverhalten bietet. Weitere Informationen finden Sie unter Verwenden von Ereignistriggern zum Steuern eines Storyboards nach dessen Start.

Weitere Informationen zu Routingereignissen

Sie können die Konzepte und Anleitungen in diesem Artikel als Ausgangspunkt beim Erstellen von benutzerdefinierten Routingereignissen in Ihren eigenen Klassen verwenden. Sie können Ihre benutzerdefinierten Ereignisse auch mit speziellen Ereignisdatenklassen und Delegaten unterstützen. Jede Klasse kann Eigentümer eines Routingereignisses sein, aber die Routingereignisse müssen von abgeleiteten Klassen von UIElement oder ContentElement ausgelöst und behandelt werden, um von Nutzen zu sein. Weitere Informationen zu benutzerdefinierten Ereignissen finden Sie unter Erstellen eines benutzerdefinierten Routingereignisses.

Siehe auch