Übersicht über Routingereignisse
In diesem Thema wird das Konzept der Routingereignisse in Windows Presentation Foundation (WPF) beschrieben. Das Thema enthält Definitionen zu den im Zusammenhang mit Routingereignissen verwendeten Begriffen, eine Beschreibung der Weiterleitung von Routingereignissen in einer Elementstruktur, eine Übersicht über die Behandlung von Routingereignissen sowie eine Einführung in die Erstellung benutzerdefinierter Routingereignisse.
Dieses Thema enthält folgende Abschnitte.
- Erforderliche Komponenten
- Was ist ein Routingereignis?
- Routingstrategien
- Gründe für die Verwendung von Routingereignissen
- Hinzufügen und Implementieren eines Ereignishandlers für ein Routingereignis
- Klassenhandler
- Angefügte Ereignisse in WPF
- Qualifizierte Ereignisnamen in XAML
- WPF-Eingabeereignisse
- EventSetters und EventTriggers
- Weitere Informationen zu Routingereignissen
- Verwandte Abschnitte
Erforderliche Komponenten
In diesem Thema wird vorausgesetzt, dass Sie über Grundkenntnisse in common language runtime (CLR) und objektorientierter Programmierung verfügen und mit dem Konzept vertraut sind, nach dem die Beziehungen zwischen WPF-Elementen als Elementstruktur begriffen werden können. Um den Beispielen in diesem Thema folgen zu können, sollten Sie zudem mit Extensible Application Markup Language (XAML) vertraut sein und sehr einfache WPF-Anwendungen und -Seiten schreiben können. Weitere Informationen finden Sie unter Exemplarische Vorgehensweise: Erste Schritte mit WPF und Übersicht über XAML (WPF).
Was ist ein Routingereignis?
Routingereignisse können entweder aus funktionaler Perspektive oder im Hinblick auf die Implementierung betrachtet werden. Hier werden der Vollständigkeit halber beide Definitionen erläutert.
Funktionale Definition: Ein Routingereignis ist ein Ereignistyp, der Handler für mehrere Listener in einer Elementstruktur aufrufen kann, und nicht nur für das Objekt, von dem das Ereignis ausgelöst wurde.
Implementierungsdefinition: Ein Routingereignis ist ein CLR-Ereignis, das auf einer Instanz der RoutedEvent-Klasse basiert und vom Windows Presentation Foundation (WPF)-Ereignissystem verarbeitet wird.
Eine typische WPF-Anwendung enthält viele Elemente. Unabhängig davon, ob sie in Code erstellt oder in XAML deklariert werden, stehen diese Elemente in einer Elementstruktur in Beziehung zueinander. Die Ereignisroute kann je nach Ereignisdefinition in zwei Richtungen verlaufen, in der Regel verläuft die Route jedoch vom Quellelement aus per "Bubbling" durch die Elementstruktur hindurch nach oben, bis der Stamm der Elementstruktur erreicht ist (normalerweise eine Seite oder ein Fenster). Sie sind möglicherweise bereits mit diesem Bubbling-Konzept vertraut, wenn Sie schon einmal mit dem DHTML-Objektmodell gearbeitet haben.
Betrachten Sie die folgende einfache Elementstruktur:
<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
<StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
<Button Name="YesButton" Width="Auto" >Yes</Button>
<Button Name="NoButton" Width="Auto" >No</Button>
<Button Name="CancelButton" Width="Auto" >Cancel</Button>
</StackPanel>
</Border>
Diese Elementstruktur erzeugt in etwa Folgendes:
In dieser vereinfachten Elementstruktur ist das Click-Ereignis eines der Button-Elemente, und der Button, auf den zuerst geklickt wird, hat die Möglichkeit, das Ereignis zu verarbeiten. Wenn jedoch kein Handler des Button auf das Ereignis angewendet wird, wird das Ereignis nach oben an den übergeordneten Button in der Elementstruktur übergeben, dem StackPanel. Möglicherweise wird das Ereignis an Border und dann bis an den Seitenstamm der Elementstruktur weitergeleitet (nicht dargestellt).
Anders ausgedrückt verläuft die Ereignisroute für dieses Click-Ereignis wie folgt:
Button-->StackPanel-->Border-->...
Szenarien auf oberster Ebene für Routingereignisse
Im Folgenden werden die Szenarien, die das Konzept der Routingereignisse motiviert haben, kurz zusammengefasst, und es wird erläutert, warum ein herkömmliches CLR-Ereignis für diese Szenarien nicht geeignet war:
Zusammensetzung und Kapselung der Steuerelemente: Viele Steuerelemente in WPF verfügen über ein umfangreiches Inhaltsmodell. So können Sie z. B. ein Bild in einen Button einfügen, wodurch die visuelle Struktur der Schaltfläche erweitert wird. Das hinzugefügte Bild darf sich jedoch nicht auf das Treffertestverhalten auswirken, das eine Schaltfläche dazu veranlasst, auf einen Click des Inhalts zu reagieren, auch wenn der Benutzer auf Pixel klickt, die technisch gesehen Teil des Bilds sind.
Einzelne Handleranfügepunkte: In Windows Forms müssten Sie denselben Handler mehrmals anfügen, um Ereignisse zu verarbeiten, die von mehreren Elementen ausgelöst werden können. Mit Routingereignissen haben Sie die Möglichkeit, diesen Handler nur einmal anzufügen, wie im vorherigen Beispiel gezeigt. Dann können Sie ggf. mithilfe von Handlerlogik ermitteln, woher das Ereignis stammt. Dies könnte z. B. der Handler für das zuvor gezeigte XAML sein:
Private Sub CommonClickHandler(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim feSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
Select Case feSource.Name
Case "YesButton"
' do something here ...
Case "NoButton"
' do something ...
Case "CancelButton"
' do something ...
End Select
e.Handled=True
End Sub
private void CommonClickHandler(object sender, RoutedEventArgs e)
{
FrameworkElement feSource = e.Source as FrameworkElement;
switch (feSource.Name)
{
case "YesButton":
// do something here ...
break;
case "NoButton":
// do something ...
break;
case "CancelButton":
// do something ...
break;
}
e.Handled=true;
}
Klassenbehandlung: Routingereignisse lassen einen statischen Handler zu, der von der Klasse definiert wird. Dieser Klassenhandler hat die Möglichkeit, ein Ereignis noch vor den angefügten Instanzhandlern zu behandeln.
Verweisen auf ein Ereignis ohne Reflektion: Bei manchen Code- und Markupverfahren muss ein bestimmtes Ereignis identifiziert werden können. Ein Routingereignis erstellt ein RoutedEvent-Feld als Bezeichner, wodurch ein robustes Verfahren der Ereignisidentifizierung bereitgestellt wird, das keine statische Reflektion oder Laufzeitreflektion erfordert.
So werden Routingereignisse implementiert
Ein Routingereignis ist ein CLR-Ereignis, das auf einer Instanz der RoutedEvent-Klasse basiert und im WPF-Ereignissystem registriert wird. Die von der Registrierung erhaltene RoutedEvent-Instanz wird in der Regel als public static readonly-Feldmember der registrierenden Klasse beibehalten, die somit "Eigentümer" des Routingereignisses ist. Die Verbindung zu dem CLR-Ereignis desselben Namens (das manchmal als "Wrapper"-Ereignis bezeichnet wird) wird durch Überschreiben der add- und remove-Implementierungen des CLR-Ereignisses hergestellt. In der Regel werden add und remove als impliziter Standard belassen, der die entsprechende sprachspezifische Ereignissyntax zum Hinzufügen und Entfernen von Handlern für dieses Ereignis verwendet. Der Mechanismus der Unterstützung und Verbindung bei Routingereignissen entspricht in etwa dem Konzept, nach dem eine Abhängigkeitseigenschaft eine CLR-Eigenschaft darstellt, die auf der DependencyProperty-Klasse basiert und im WPF-Eigenschaftensystem registriert wird.
Im folgenden Beispiel wird die Deklaration eines benutzerdefinierten Tap-Routingereignisses veranschaulicht und gezeigt, wie das RoutedEvent-Bezeichnerfeld und die add- und remove-Implementierungen für das Tap CLR-Ereignis registriert und verfügbar gemacht werden.
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent("Tap", RoutingStrategy.Bubble, GetType(RoutedEventHandler), GetType(MyButtonSimple))
' Provide CLR accessors for the event
Public Custom Event Tap As RoutedEventHandler
AddHandler(ByVal value As RoutedEventHandler)
Me.AddHandler(TapEvent, value)
End AddHandler
RemoveHandler(ByVal value As RoutedEventHandler)
Me.RemoveHandler(TapEvent, value)
End RemoveHandler
RaiseEvent(ByVal sender As Object, ByVal e As RoutedEventArgs)
Me.RaiseEvent(e)
End RaiseEvent
End Event
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
"Tap", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyButtonSimple));
// Provide CLR accessors for the event
public event RoutedEventHandler Tap
{
add { AddHandler(TapEvent, value); }
remove { RemoveHandler(TapEvent, value); }
}
Routingereignishandler und XAML
Um einen Handler für ein Ereignis mit XAML hinzuzufügen, deklarieren Sie den Ereignisnamen als Attribut des Elements, das einen Ereignislistener darstellt. Der Wert des Attributs ist der Name der implementierten Handlermethode, die in der partiellen Klasse der Code-Behind-Datei enthalten sein muss.
<Button Click="b1SetColor">button</Button>
Die XAML-Syntax zum Hinzufügen standardmäßiger CLR-Ereignishandler entspricht dem Hinzufügen von Routingereignishandlern, da Sie im Grunde Handler zum CLR-Ereigniswrapper hinzufügen, der auf einer Routingereignisimplementierung basiert. Weitere Informationen zum Hinzufügen von Ereignishandlern in XAML finden Sie unter Übersicht über XAML (WPF).
Routingstrategien
Routingereignisse verwenden eine von drei Routingstrategien:
Bubbling: Es werden Ereignishandler auf der Ereignisquelle aufgerufen. Das Routingereignis wird dann an die folgenden übergeordneten Elemente weitergeleitet, bis der Stamm der Elementstruktur erreicht ist. Die meisten Routingereignisse verwenden die Bubbling-Routingstrategie. Bubbling-Routingereignisse werden in der Regel dazu verwendet, um Eingaben oder Änderungen des Betriebszustands von verschiedenen Steuerelementen oder anderen Benutzeroberflächenelementen zu melden.
Direkt: Nur das Quellelement selbst hat die Möglichkeit, durch Aufrufen von Handlern zu reagieren. Dies entspricht dem "Routing", das von Windows Forms für Ereignisse verwendet wird. Im Gegensatz zu standardmäßigen CLR-Ereignissen unterstützen direkte Routingereignisse jedoch die Klassenbehandlung (die Klassenbehandlung wird in einem der folgenden Abschnitte erläutert) und können von EventSetter und EventTrigger verwendet werden.
Tunneling: Zunächst werden Ereignishandler am Elementstrukturstamm aufgerufen. Anschließend wird das Routingereignis über die nachfolgenden untergeordneten Elemente in der Route bis zu dem Knotenelement weitergeleitet, das die Quelle des Routingereignisses darstellt (das Element, von dem das Routingereignis ausgelöst wurde). Tunneling-Routingereignisse werden oft als Teil der Zusammensetzung für ein Steuerelement verwendet oder verarbeitet, sodass Ereignisse von zusammengesetzten Teilen gezielt unterdrückt oder durch Ereignisse ersetzt werden, die für das gesamte Steuerelement spezifisch sind. In WPF bereitgestellte Eingabeereignisse werden oft als Tunneling/Bubbling-Paar implementiert. Aufgrund einer Namenskonvention für die Paare werden Tunneling-Ereignisse manchmal auch als Vorschauereignisse bezeichnet.
Gründe für die Verwendung von Routingereignissen
Für Sie als Anwendungsentwickler ist es nicht immer relevant, dass das behandelte Ereignis als Routingereignis implementiert wird. Routingereignisse verfügen zwar über ein spezielles Verhalten, dieses macht sich jedoch kaum bemerkbar, wenn Sie ein Ereignis auf dem Element behandeln, von dem es ausgelöst wird.
In den folgenden Szenarien sind Routingereignisse besonders nützlich: Definieren gemeinsamer Handler an einem gemeinsamen Stamm, Zusammensetzen eines eigenen Steuerelements oder Definieren einer eigenen benutzerdefinierten Steuerelement-Klasse.
Routingereignislistener und Routingereignisquellen müssen nicht über ein gemeinsames Ereignis in ihrer Hierarchie verfügen. Jedes UIElement oder ContentElement kann ein Ereignislistener für jedes beliebige Routingereignis sein. Deshalb können Sie alle innerhalb des API-Workingsets verfügbaren Routingereignisse als konzeptionelle "Schnittstelle" verwenden, über die unterschiedliche Elemente in der Anwendung Ereignisinformationen austauschen können. Dieses "Schnittstellen"-Konzept für Routingereignisse gilt vor allem für Eingabeereignisse.
Routingereignisse können auch für die Kommunikation innerhalb der Elementstruktur verwendet werden, da die Ereignisdaten für das Ereignis an jedes Element auf der Route weitergegeben werden. Wenn ein Element z. B. eine Änderung an den Ereignisdaten vornimmt, ist diese Änderung für das nächste Element auf der Route verfügbar.
Abgesehen vom Routingaspekt gibt es zwei weitere Gründe, aus denen ein beliebiges WPF-Ereignis als Routingereignis anstatt als standardmäßiges CLR-Ereignis implementiert werden könnte. Wenn Sie Ihre eigenen Ereignisse implementieren, könnten auch folgende Prinzipien für Sie von Interesse sein:
Bestimmte Stil- und Vorlagenfunktionen in WPF, wie EventSetter und EventTrigger, müssen auf ein Routingereignis verweisen. Dieses Ereignisbezeichner-Szenario wurde bereits weiter oben beschrieben.
Routingereignisse unterstützen einen Klassenbehandlungsmechanismus, mit dem die Klasse statische Methoden angeben kann, die Routingereignisse verarbeiten können, bevor registrierte Instanzhandler auf sie zugreifen können. Dieser Mechanismus ist für Steuerelemententwürfe besonders nützlich, da die Klasse ereignisgesteuerte Klassenverhalten erzwingen kann, die nicht unbeabsichtigt durch Behandlung eines Ereignisses für eine Instanz unterdrückt werden können.
Jede der oben erwähnten Überlegungen wird in einem separaten Abschnitt dieses Themas erläutert.
Hinzufügen und Implementieren eines Ereignishandlers für ein Routingereignis
Zum Hinzufügen eines Ereignishandlers in XAML fügen Sie einfach den Ereignisnamen als Attribut zu einem Element hinzu und legen den Attributwert als Namen des Ereignisses des Ereignishandlers fest, der einen entsprechenden Delegaten implementiert, wie im folgenden Beispiel veranschaulicht.
<Button Click="b1SetColor">button</Button>
b1SetColor ist der Name des implementierten Handlers, der den Code zur Behandlung des Click-Ereignisses enthält. b1SetColor muss über dieselbe Signatur verfügen wie der RoutedEventHandler-Delegat, bei dem es sich um das Ereignishandlerobjekt für das Click-Ereignis handelt. Der erste Parameter aller Routingereignishandler-Delegaten gibt das Element an, zu dem ein Ereignishandler hinzugefügt wird, und der zweite Parameter gibt die Daten für das Ereignis an.
Private Sub b1SetColor(ByVal sender As Object, ByVal args As RoutedEventArgs)
'logic to handle the Click event
...
End Sub
void b1SetColor(object sender, RoutedEventArgs args)
{
//logic to handle the Click event
...
}
RoutedEventHandler ist der einfache Routingereignishandler-Delegat. Bei Routingereignissen, die speziell auf bestimmte Szenarien ausgelegt sind, können die für die Routingereignishandler zu verwendenden Delegaten auch komplexer sein, sodass sie spezialisierte Ereignisdaten übertragen können. So könnten Sie z. B. in einem üblichen Eingabeszenario ein DragEnter-Routingereignis behandeln. Der Handler sollte den DragEventHandler-Delegaten implementieren. Durch Verwendung des spezifischsten Delegaten können Sie DragEventArgs im Handler verarbeiten und die Data-Eigenschaft lesen, in der die Zwischenablagennutzlast des Ziehvorgangs enthalten ist.
Ein vollständiges Beispiel zum Hinzufügen eines Ereignishandlers zu einem Element mithilfe von XAML finden Sie unter Gewusst wie: Behandeln eines Routingereignisses.
Das Hinzufügen eines Handlers für ein Routingereignis in einer in Code erstellten Anwendung ist nicht schwierig. Routingereignishandler können immer mithilfe einer Hilfsmethode AddHandler hinzugefügt werden (hierbei handelt es sich um dieselbe Methode, die von der vorhandenen zugrunde liegenden Implementierung für add aufgerufen wird). Vorhandene WPF-Routingereignisse verfügen jedoch in der Regel über zugrunde liegende Implementierungen der add- und remove-Logik, die das Hinzufügen der Handler für Routingereignisse über eine sprachspezifische Ereignissyntax ermöglichen, deren Syntax intuitiver ist als die Hilfsmethode. Die Verwendung der Hilfsmethode wird im folgenden Beispiel veranschaulicht:
Private Sub MakeButton()
Dim b2 As New Button()
b2.AddHandler(Button.ClickEvent, New RoutedEventHandler(AddressOf Onb2Click))
End Sub
Private Sub Onb2Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
'logic to handle the Click event
End Sub
void MakeButton()
{
Button b2 = new Button();
b2.AddHandler(Button.ClickEvent, new RoutedEventHandler(Onb2Click));
}
void Onb2Click(object sender, RoutedEventArgs e)
{
//logic to handle the Click event
}
Im nächsten Beispiel ist die C#-Operatorsyntax dargestellt (die Operatorsyntax von Visual Basic weicht aufgrund der Dereferenzierungsbehandlung leicht davon ab):
Private Sub MakeButton2()
Dim b2 As New Button()
AddHandler b2.Click, AddressOf Onb2Click2
End Sub
Private Sub Onb2Click2(ByVal sender As Object, ByVal e As RoutedEventArgs)
'logic to handle the Click event
End Sub
void MakeButton2()
{
Button b2 = new Button();
b2.Click += new RoutedEventHandler(Onb2Click2);
}
void Onb2Click2(object sender, RoutedEventArgs e)
{
//logic to handle the Click event
}
Ein Beispiel für das Hinzufügen eines Ereignishandlers in Code finden Sie unter Gewusst wie: Hinzufügen eines Ereignishandlers mithilfe von Code.
Wenn Sie Visual Basic verwenden, können Sie Handler auch mit dem Handles-Schlüsselwort als Teil der Handlerdeklaration hinzufügen. Weitere Informationen finden Sie unter Visual Basic- und WPF-Ereignisbehandlung.
Das Handled-Konzept
Alle Routingereignisse verwenden eine gemeinsame Ereignisdaten-Basisklasse, RoutedEventArgs. RoutedEventArgs definiert die Handled-Eigenschaft, die einen booleschen Wert annimmt. Die Handled-Eigenschaft soll es Ereignishandlern auf der Route ermöglichen, das Routingereignis als behandelt zu markieren, indem der Wert von Handled in true geändert wird. Nach der Verarbeitung durch den Handler eines Elements auf der Route werden die gemeinsamen Ereignisdaten erneut jedem Listener auf der Route gemeldet.
Der Wert von Handled wirkt sich darauf aus, wie ein Routingereignis während der Weiterleitung durch die Route gemeldet oder verarbeitet wird. Wenn Handled in den Ereignisdaten für ein Routingereignis true ist, werden Handler, die dieses Routingereignis auf anderen Elementen überwachen, in der Regel für diese spezifische Ereignisinstanz nicht mehr aufgerufen. Das gilt sowohl für in XAML angefügte Handler als auch für Handler, die durch eine sprachspezifische Syntax zum Hinzufügen von Routingereignissen wie += oder Handles hinzugefügt werden. Bei den häufigsten Handlerszenarien wird durch Festlegen von Handled auf true das Routing sowohl für Tunneling- als auch für Bubbling-Routen sowie für jedes Ereignis, das an einem Punkt auf der Route durch einen Klassenhandler behandelt wird, "gestoppt".
Es gibt jedoch einen "handledEventsToo"-Mechanismus, der Listenern das Ausführen von Handlern als Reaktion auf Routingereignisse ermöglicht, wenn Handled in den Ereignisdaten den Wert true hat. Anders ausgedrückt wird die Ereignisroute durch Markieren der Ereignisdaten als behandelt nicht wirklich gestoppt. Sie können den handledEventsToo-Mechanismus nur in Code oder in einem EventSetter verwenden:
Rufen Sie in Code anstelle einer für allgemeine CLR-Ereignisse verwendbaren sprachspezifischen Ereignissyntax die WPF-Methode AddHandler(RoutedEvent, Delegate, Boolean) zum Hinzufügen des Handlers auf. Geben Sie den Wert von handledEventsToo als true an.
Legen Sie in einem EventSetter das HandledEventsToo-Attribut als true fest.
Zusätzlich zu dem Verhalten, das der Handled-Status in Routingereignissen erzeugt, wirkt sich das Konzept von Handled auf die Art und Weise aus, wie Sie Ihre Anwendung entwerfen und den Ereignishandlercode schreiben. Handled kann als einfaches Protokoll begriffen werden, das von Routingereignissen verfügbar gemacht wird. Sie können selbst entscheiden, wie Sie dieses Protokoll genau verwenden, der Konzeptentwurf für die Verwendung des Werts von Handled ist jedoch auf folgende Verwendung ausgerichtet:
Wenn ein Routingereignis als behandelt markiert ist, muss es nicht von anderen Elementen auf dieser Route erneut verarbeitet werden.
Wenn ein Ereignis nicht als behandelt markiert ist, haben entweder frühere Listener auf der Route keinen Handler registriert, oder die registrierten Handler haben die Ereignisdaten nicht geändert und Handled nicht auf true festgelegt. (Es ist natürlich auch möglich, dass der aktuelle Listener den ersten Punkt in der Route darstellt.) Handler auf dem aktuellen Listener verfügen jetzt über drei mögliche Vorgehensweisen:
Es wird überhaupt keine Aktion ausgeführt, das Ereignis wird nicht behandelt, und das Ereignis wird an den nächsten Listener weitergeleitet.
Code wird als Reaktion auf das Ereignis ausgeführt, die durchgeführte Aktion war jedoch nicht umfassend genug, um die Markierung des Ereignisses als behandelt zu rechtfertigen. Das Ereignis wird zum nächsten Listener weitergeleitet.
Code wird als Reaktion auf das Ereignis ausgeführt. Das Ereignis wird in den an den Handler übergebenen Ereignisdaten als behandelt markiert, da die durchgeführte Aktion ausreicht, um die Markierung des Ereignisses als behandelt zu rechtfertigen. Das Ereignis wird dennoch an den nächsten Listener weitergeleitet, jedoch mit Handled=true in den Ereignisdaten, sodass nur handledEventsToo-Listener die Möglichkeit haben, weitere Handler aufzurufen.
Dieser Konzeptentwurf wird durch das oben beschriebene Routingverhalten unterstützt: Es ist schwieriger (wenn auch in Code oder Stilen durchaus möglich), Handler für Routingereignisse anzufügen, wenn ein vorhergehender Handler auf der Route den Wert für Handled bereits in true geändert hat.
Weitere Informationen zu Handled, zur Klassenbehandlung von Routingereignissen sowie zu Empfehlungen dazu, wann es angemessen ist, ein Routingereignis als Handled zu markieren, finden Sie unter Markieren von Routingereignissen als behandelt und Klassenbehandlung.
In Anwendungen ist es recht üblich, ein Bubbling-Routingereignis nur für das Objekt zu behandeln, von dem es ausgelöst wurde, ohne die Routingeigenschaften des Ereignisses weiter zu beachten. Es empfiehlt sich aber dennoch, das Routingereignis in den Ereignisdaten als behandelt zu markieren, um unvorhergesehene Nebeneffekte für den Fall zu verhindern, dass an ein weiter oben in der Elementstruktur befindliches Element ein Handler für dasselbe Routingereignis angefügt ist.
Klassenhandler
Wenn Sie eine Klasse definieren, bei der auf irgendeine Weise von DependencyObject abgeleitet wird, können Sie auch einen Klassenhandler für ein Routingereignis definieren und anfügen, bei dem es sich um einen deklarierten oder geerbten Ereignismember der Klasse handelt. Klassenhandler werden noch vor den an eine Instanz dieser Klasse angefügten Instanzlistener-Handlern aufgerufen, wenn ein Routingereignis eine Elementinstanz auf seiner Route erreicht.
Manche WPF-Steuerelemente verfügen über eine inhärente Klassenbehandlung für bestimmte Routingereignisse. Hierdurch könnte der Anschein entstehen, dass das Routingereignis niemals ausgelöst wird, tatsächlich wird es jedoch durch eine Klasse behandelt, und das Routingereignis kann trotzdem von den Instanzhandlern behandelt werden, wenn Sie bestimmte Verfahrensweisen befolgen. Zudem machen viele Basisklassen und Steuerelemente virtuelle Methoden verfügbar, mit denen das Klassenbehandlungsverhalten überschrieben werden kann. Weitere Informationen dazu, wie eine unerwünschte Klassenbehandlung umgangen werden kann und wie Sie Ihre eigene Klassenbehandlung in einer benutzerdefinierten Klasse definieren, finden Sie unter Markieren von Routingereignissen als behandelt und Klassenbehandlung.
Angefügte Ereignisse in WPF
Die XAML-Sprache definiert auch einen speziellen Ereignistyp, der als angefügtes Ereignis bezeichnet wird. Mit einem angefügten Ereignis können Sie einen Handler für ein bestimmtes Ereignis zu einem beliebigen Element hinzufügen. Das Element, von dem das Ereignis behandelt wird, muss das angefügte Ereignis nicht definieren oder erben, und weder das Objekt, von dem das Ereignis potenziell ausgelöst wird, noch die behandelnde Zielinstanz müssen dieses Ereignis als Klassenmember definieren oder auf andere Weise "besitzen".
Das WPF-Eingabesystem nutzt angefügte Ereignisse in großem Umfang. Fast alle dieser angefügten Ereignisse werden jedoch über Basiselemente weitergeleitet. Diese Eingabeereignisse erscheinen dann als entsprechende nicht angefügte Routingereignisse, die Member der Basiselementklasse sind. So kann beispielsweise das zugrunde liegende angefügte Ereignis Mouse.MouseDown einfacher auf einem gegebenen UIElement behandelt werden, wenn MouseDown auf diesem UIElement verwendet wird, anstatt die Syntax für angefügte Ereignisse in XAML oder im Code zu verwenden.
Weitere Informationen zu angefügten Ereignissen in WPF finden Sie unter Übersicht über angefügte Ereignisse.
Qualifizierte Ereignisnamen in XAML
Eine andere Syntaxverwendung, die der typename.eventname-Syntax für angefügte Ereignisse ähnelt, streng genommen jedoch keine angefügten Ereignisse verwendet, besteht im Hinzufügen von Handlern für Routingereignisse, die von untergeordneten Elementen ausgelöst werden. Um das Ereignisrouting zu nutzen, fügen Sie die Handler an ein gemeinsames übergeordnetes Element an, selbst wenn das relevante Routingelement kein Member des gemeinsamen übergeordneten Elements ist. Betrachten Sie erneut folgendes Beispiel:
<Border Height="50" Width="300" BorderBrush="Gray" BorderThickness="1">
<StackPanel Background="LightGray" Orientation="Horizontal" Button.Click="CommonClickHandler">
<Button Name="YesButton" Width="Auto" >Yes</Button>
<Button Name="NoButton" Width="Auto" >No</Button>
<Button Name="CancelButton" Width="Auto" >Cancel</Button>
</StackPanel>
</Border>
Hier handelt es sich bei dem Listener des übergeordneten Elements, für das der Handler hinzugefügt wird, um ein StackPanel. Es fügt jedoch einen Handler für ein deklariertes Routingereignis hinzu und wird nicht von der Button-Klasse ausgelöst (eigentlich ButtonBase, für Button durch Vererbung verfügbar). Button ist "Eigentümer" des Ereignisses, das Routingereignissystem lässt jedoch zu, dass Handler eines beliebigen Routingereignisses an jeden UIElement- oder ContentElement-Instanzlistener angefügt werden, die andernfalls Listener für ein common language runtime (CLR)-Ereignis anfügen könnten. Der standardmäßige xmlns-Namespace für diese qualifizierten Ereignisattributnamen ist in der Regel der standardmäßige WPF-xmlns-Namespace, Sie können jedoch auch Namespaces mit Präfix für benutzerdefinierte Routingereignisse verwenden. Weitere Informationen zu xmlns finden Sie unter XAML-Namespaces und Namespacezuordnung für WPF-XAML.
WPF-Eingabeereignisse
Häufig werden Routingereignisse innerhalb der WPF-Plattform für Eingabeereignisse verwendet. In WPF wird dem Namen von Tunnelingroutingereignissen gemäß Konvention das Wort "Preview" als Präfix vorangestellt. Eingabeereignisse liegen in der Regel als Paar vor, das aus einem Bubbling- und einem Tunneling-Ereignis besteht. So verfügen z. B. das KeyDown-Ereignis und das PreviewKeyDown-Ereignis über dieselbe Signatur, wobei ersteres das Bubbling-Eingabeereignis und letzteres das Tunneling-Eingabeereignis darstellt. Manchmal haben Ereignisse nur eine Bubbling-Version oder auch nur eine direkte Routing-Version. In der Dokumentation verweisen Themen, die Routingereignisse betreffen, auf ähnliche Routingereignisse mit unterschiedlichen Routingstrategien, falls solche Routingereignisse vorhanden sind, und in den Abschnitten auf den Seiten zu verwalteten Verweisen wird die Routingstrategie für jedes Routingereignis erläutert.
WPF-Eingabeereignisse, die als Paar vorliegen, werden so implementiert, dass durch eine einzige Benutzereingabeaktion, wie z. B. ein Mausklick, beide Routingereignisse des Paars nacheinander ausgelöst werden. Zuerst wird das Tunneling-Ereignis ausgelöst und durchläuft seine Route. Dann wird das Bubbling-Ereignis ausgelöst und durchläuft seine Route. Die beiden Ereignisse verwenden dieselbe Ereignisdateninstanz, da der RaiseEvent-Methodenaufruf in der implementierenden Klasse, der das Bubbling-Ereignis auslöst, die Ereignisdaten des Tunneling-Ereignisses überwacht und im neuen ausgelösten Ereignis verwendet. Listener mit Handlern für das Tunneling-Ereignis haben als erstes die Möglichkeit, das Routingereignis als behandelt zu markieren (zuerst Klassenhandler und dann Instanzhandler). Wenn ein Element auf der Tunneling-Route das Routingereignis als behandelt markiert, werden die bereits behandelten Ereignisdaten an das Bubbling-Ereignis gesendet, und die für die entsprechenden Bubbling-Eingabeereignisse angefügten typischen Handler werden nicht aufgerufen. Nach außen hat es den Anschein, als ob das behandelte Bubbling-Ereignis überhaupt nicht ausgelöst wurde. Dieses Behandlungsverhalten ist bei der Zusammensetzung von Steuerelementen hilfreich, wenn alle auf Treffertest oder Fokus basierenden Eingabeereignisse vom endgültigen Steuerelement und nicht von dessen einzelnen Teilen gemeldet werden sollen. Das endgültige Steuerelement befindet sich in der Zusammensetzung näher am Stamm und hat deshalb die Möglichkeit, das Tunneling-Ereignis zuerst zu behandeln und dieses Routingereignis als Teil des Codes, auf dem die Steuerelementklasse basiert, ggf. durch ein steuerelementspezifischeres Ereignis zu "ersetzen".
Folgendes Eingabeereignisbeispiel veranschaulicht, wie die Verarbeitung von Eingabeereignissen funktioniert. In der folgenden Strukturabbildung ist leaf element #2 sowohl die Quelle eines PreviewMouseDown- als auch eines MouseDown-Ereignisses.
Bubbling und Tunneling von Eingabeereignissen
Die Ereignisverarbeitung erfolgt in der folgenden Reihenfolge:
PreviewMouseDown (Tunneling) auf dem Stammelement.
PreviewMouseDown (Tunneling) auf Zwischenelement Nr. 1.
PreviewMouseDown (Tunneling) auf Quellelement Nr. 2.
MouseDown (Bubbling) auf Quellelement Nr. 2.
MouseDown (Bubbling) auf Zwischenelement Nr. 1.
MouseDown (Bubbling) auf dem Stammelement.
Ein Routingereignishandler-Delegat stellt Verweise auf zwei Objekte bereit: das Objekt, von dem das Ereignis ausgelöst wurde, und das Objekt, von dem der Handler aufgerufen wurde. Das Objekt, an dem der Handler aufgerufen wurde, ist das Objekt, das vom sender-Parameter gemeldet wird. Das Objekt, an dem das Ereignis erstmals ausgelöst wurde, wird von der Source-Eigenschaft in den Ereignisdaten gemeldet. Ein Routingereignis kann auch von demselben Objekt ausgelöst und behandelt werden, wobei sender und Source identisch sind (dies ist bei Schritt 3 und 4 in der Beispielliste für die Ereignisbearbeitung der Fall).
Aufgrund des Tunneling und Bubbling erhalten übergeordnete Elemente Eingabeereignisse, wenn die Source eines ihrer untergeordneten Elemente darstellt. Wenn Sie wissen müssen, welches das Quellelement ist, können Sie das Quellelement durch Zugreifen auf die Source-Eigenschaft ermitteln.
Normalerweise werden keine weiteren Handler aufgerufen, sobald das Eingabeereignis als Handled markiert wurde. In der Regel sollten Sie Eingabeereignisse als behandelt markieren, wenn ein Handler aufgerufen wird, der sich auf die anwendungsspezifische logische Behandlung der Bedeutung des Eingabeereignisses bezieht.
Eine Ausnahme zu dieser allgemeinen Aussage zum Handled-Status besteht darin, dass Eingabeereignishandler, die so registriert wurden, dass sie den Handled-Status der Ereignisdaten gezielt ignorieren, auf beiden Routen dennoch ausgelöst werden. Weitere Informationen finden Sie unter Vorschauereignisse oder unter Markieren von Routingereignissen als behandelt und Klassenbehandlung.
Das Modell der gemeinsam verwendeten Ereignisdaten zwischen Tunneling- und Bubbling-Ereignissen sowie die sequenzielle Auslösung erst der Tunneling- und dann der Bubbling-Ereignisse ist kein Konzept, das für alle Routingereignisse gilt. Die spezifische Implementierung des Verhaltens hängt davon ab, wie die Eingabeereignispaare von WPF-Eingabegeräten ausgelöst und verbunden werden. Die Implementierung eigener Eingabeereignisse stellt ein erweitertes Szenario dar, Sie können sich jedoch auch bei eigenen Eingabeereignissen nach diesem Modell richten.
Manche Klassen wählen die Klassenbehandlung für bestimmte Eingabeereignisse, in der Regel mit dem Zweck, die Bedeutung eines bestimmten benutzergesteuerten Eingabeereignisses innerhalb dieses Steuerelements neu zu definieren und ein neues Ereignis auszulösen. Weitere Informationen finden Sie unter Markieren von Routingereignissen als behandelt und Klassenbehandlung.
Weitere Informationen zur Eingabe und zur Interaktion zwischen Eingaben und Ereignissen in typischen Anwendungsszenarien finden Sie unter Übersicht über die Eingabe.
EventSetters und EventTriggers
In Stilen können Sie manche vordeklarierte XAML-Ereignisbehandlungssyntax mithilfe eines EventSetter im Markup einschließen. Wenn der Stil angewendet wird, wird der referenzierte Handler zur formatierten Instanz hinzugefügt. Sie können einen EventSetter nur für ein Routingereignis deklarieren. Im Folgenden finden Sie ein Beispiel. Beachten Sie, dass sich die hier beschriebene b1SetColor-Methode in einer Code-Behind-Datei befindet.
<StackPanel
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.EventOvw2"
Name="dpanel2"
Initialized="PrimeHandledToo"
>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<EventSetter Event="Click" Handler="b1SetColor"/>
</Style>
</StackPanel.Resources>
<Button>Click me</Button>
<Button Name="ThisButton" Click="HandleThis">
Raise event, handle it, use handled=true handler to get it anyway.
</Button>
</StackPanel>
Der Vorteil hierbei besteht darin, dass der Stil zahlreiche andere Informationen enthält, die auf alle anderen Schaltflächen in der Anwendung angewendet werden könnten, und wenn der EventSetter Teil dieses Stils ist, wird die Wiederverwendung von Code selbst auf Markupebene erleichtert. Zudem abstrahiert ein EventSetter Methodennamen für Handler, die einen Schritt von der allgemeinen Anwendung und dem Seitenmarkup entfernt sind.
Eine andere spezialisierte Syntax, in der die Routingereignis- und Animationsfeatures von WPF kombiniert werden, ist ein EventTrigger. Wie bei einem EventSetter dürfen für einen EventTrigger nur Routingereignisse verwendet werden. In der Regel wird ein EventTrigger als Teil eines Stils deklariert, ein EventTrigger kann jedoch auch als Teil der Triggers-Auflistung oder in einer ControlTemplate für Elemente auf Seitenebene deklariert werden. Mit einem EventTrigger können Sie ein Storyboard angeben, das immer dann ausgeführt wird, wenn ein Routingereignis ein Element auf seiner Route erreicht hat, das einen EventTrigger für dieses Ereignis deklariert. Der Vorteil eines EventTrigger gegenüber der bloßen Behandlung des Ereignisses, das ein vorhandenes Storyboard startet, besteht darin, dass ein EventTrigger eine bessere Steuerung des Storyboards und seines Laufzeitverhaltens ermöglicht. Weitere Informationen finden Sie unter Gewusst wie: Verwenden von Ereignistriggern zum Steuern eines Storyboards nach dessen Start.
Weitere Informationen zu Routingereignissen
In diesem Thema werden Routingereignisse vor allem durch Beschreiben der grundsätzlichen Konzepte erläutert. Außerdem erhalten Sie Hinweise dazu, wie und wann Sie auf die Routingereignisse reagieren sollten, die bereits in den verschiedenen grundlegenden Elementen und Steuerelementen enthalten sind. Sie können jedoch ein eigenes Routingereignis für Ihre benutzerdefinierte Klasse erstellen, einschließlich der notwendigen Hilfsmittel, wie z. B. spezialisierte Ereignisdatenklassen und -delegaten. Jede Klasse kann Eigentümer des Routingereignisses sein, um einen echten Nutzen zu erbringen, müssen Routingereignisse jedoch von abgeleiteten UIElement- oder ContentElement-Klassen ausgelöst und behandelt werden. Weitere Informationen zu benutzerdefinierten Ereignissen finden Sie unter Gewusst wie: Erstellen eines benutzerdefinierten Routingereignisses.
Siehe auch
Referenz
Konzepte
Markieren von Routingereignissen als behandelt und Klassenbehandlung