Übersicht über Ereignisse und Routingereignisse
Wichtige APIs
Wir beschreiben das Programmierkonzept von Ereignissen in einer Windows-Runtime-App bei Verwendung von C#, Visual Basic oder Visual C++-Komponentenerweiterungen (C++/CX) als Programmiersprache und XAML für Ihre UI-Definition. Sie können im Rahmen der Deklarationen für UI-Elemente Handler für Ereignisse in XAML zuweisen. Alternativ können Sie Handler im Code hinzufügen. Die Windows-Runtime unterstützt Routingereignisse: Bestimmte Eingabeereignisse und Datenereignisse können von anderen Objekten behandelt werden als dem Objekt, von dem das Ereignis ausgelöst wurde. Routingereignisse sind hilfreich, wenn Sie Steuerelementvorlagen definieren oder Seiten oder Layoutcontainer verwenden.
Ereignisse als Programmierkonzept
Grundsätzlich sind Ereigniskonzepte bei der Programmierung einer Windows-Runtime-App mit dem Ereignismodell in den meisten gängigen Programmiersprachen vergleichbar. Wenn Sie bereits mit Microsoft .NET- oder C++-Ereignissen vertraut sind, kommen Sie sofort zurecht. Für verschiedene allgemeine Aufgaben, wie das Hinzufügen von Handlern, müssen Sie jedoch nicht allzu viel über Ereignismodellkonzepte wissen.
Wenn Sie C#, Visual Basic oder C++/CX als Programmiersprache verwenden, wird die UI im Markup (XAML) definiert. In der XAML-Markupsyntax ähneln einige der Prinzipien, nach denen UI-Ereignisse zwischen Markupelementen und Laufzeitcodeentitäten verbunden werden, denen anderer Webtechnologien (z. B. ASP.NET oder HTML5).
Hinweis: Der Code, der die Laufzeitlogik für eine XAML-definierte Benutzeroberfläche bereitstellt, wird häufig als CodeBehind- oder CodeBehind-Datei bezeichnet. In den Projektmappenansichten von Microsoft Visual Studio wird diese Beziehung grafisch dargestellt. Dabei ist die CodeBehind-Datei eine abhängige und geschachtelte Datei zu der XAML-Seite, auf die sie sich bezieht.
Button.Click: Einführung in Ereignisse und XAML
Zu den häufigsten Programmieraufgaben für eine Windows-Runtime-App gehört es, Benutzereingaben für die UI zu erfassen. Ihre Benutzeroberfläche kann z. B. über eine Schaltfläche verfügen, auf die der Benutzer klicken muss, um Informationen zu senden oder den Zustand zu ändern.
Sie definieren die UI für Ihre Windows-Runtime-App mittels XAML-Generierung. Bei diesem XAML handelt es sich meist um die Ausgabe einer Designoberfläche in Visual Studio. Sie können das XAML auch in einem Nur-Text-Editor oder im XAML-Editor eines Drittanbieters schreiben. Beim Generieren dieses XAML können Sie Ereignishandler für einzelne UI-Elemente verknüpfen, während Sie gleichzeitig alle anderen XAML-Attribute definieren, die Eigenschaftswerte dieses UI-Elements einrichten.
Zur Ereignisverknüpfung in XAML gehört, dass Sie den Namen der Handlermethode, die Sie bereits definiert haben oder später im CodeBehind definieren werden, als Zeichenfolge angeben. Diese XAML definiert beispielsweise ein Button-Objekt mit anderen Eigenschaften (x:Name-Attribut, Content)die als Attribute zugewiesen sind, und verknüpft einen Handler für das Click-Ereignis der Schaltfläche durch einen Verweis auf eine Methode namens ShowUpdatesButton_Click
.
<Button x:Name="showUpdatesButton"
Content="{Binding ShowUpdatesText}"
Click="ShowUpdatesButton_Click"/>
Tipp: Die Ereignisverknüpfung ist ein Begriff der Programmiersprache. Er bezieht sich auf den Prozess oder Code, mit dem Sie angeben, dass Vorkommen eines Ereignisses eine benannte Handlermethode aufrufen sollen. In den meisten prozeduralen Codemodellen handelt es sich bei der Ereignisverknüpfung um impliziten oder expliziten „AddHandler“-Code, der sowohl das Ereignis als auch die Methode benennt und in der Regel eine Zielobjektinstanz verwendet. In XAML ist „AddHandler“ implizit und die Ereignisverknüpfung umfasst nur das Benennen des Ereignisses als Attributnamen eines Objektelement und das Benennen des Handlers als Wert dieses Attributs.
Sie schreiben den eigentlichen Handler in der Programmiersprache, die Sie für Ihren gesamten App-Code oder CodeBehind verwenden. Mit dem Attribut Click="ShowUpdatesButton_Click"
haben Sie einen Vertrag erstellt, mit dem beim Kompilieren des Markups und Analysieren des XAML sowohl der XAML-Markupkompilierschritt in der Erstellungsaktion Ihrer IDE als auch die mögliche XAML-Analyse beim Laden der App eine Methode mit dem Namen ShowUpdatesButton_Click
finden können, die Teil des App-Codes ist. ShowUpdatesButton_Click
muss eine Methode darstellen, die eine kompatible Methodensignatur (basierend auf einem Delegaten) für jeden Handler des Click-Ereignisses implementiert. Dieser Code definiert z. B. den ShowUpdatesButton_Click
-Handler.
private void ShowUpdatesButton_Click (object sender, RoutedEventArgs e)
{
Button b = sender as Button;
//more logic to do here...
}
Private Sub ShowUpdatesButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim b As Button = CType(sender, Button)
' more logic to do here...
End Sub
void winrt::MyNamespace::implementation::BlankPage::ShowUpdatesButton_Click(Windows::Foundation::IInspectable const& sender, Windows::UI::Xaml::RoutedEventArgs const& e)
{
auto b{ sender.as<Windows::UI::Xaml::Controls::Button>() };
// More logic to do here.
}
void MyNamespace::BlankPage::ShowUpdatesButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
Button^ b = (Button^) sender;
//more logic to do here...
}
In diesem Beispiel basiert die ShowUpdatesButton_Click
-Methode auf dem RoutedEventHandler-Delegaten. Sie wissen, dass dies der zu verwendende Delegat ist, da dieser Delegat in der Syntax für die Click-Methode angezeigt wird.
Tipp: Visual Studio bietet eine bequeme Möglichkeit, den Ereignishandler zu benennen und die Handlermethode beim Bearbeiten von XAML zu definieren. Wenn Sie den Attributnamen des Ereignisses im XAML-Text-Editor angeben, warten Sie einen Moment, bis eine Microsoft IntelliSense-Liste angezeigt wird. Wenn Sie in der Liste auf <Neuer Ereignishandler> klicken, schlägt Microsoft Visual Studio einen Methodennamen basierend auf dem x:Name (oder Typnamen) des Elements, der Ereignisnamen und eine numerischen Suffix vor. Sie können dann mit der rechten Maustaste auf den ausgewählten Ereignishandlernamen klicken und auf Zu Ereignishandler navigieren klicken. Dadurch wird direkt zur neu eingefügten Ereignishandlerdefinition navigiert, wie in der Code-Editor-Ansicht der CodeBehind-Datei für die XAML-Seite zu sehen ist. Der Ereignishandler verfügt bereits über die richtige Signatur, einschließlich des sender-Parameters und der Ereignisdatenklasse, die das Ereignis verwendet. Wenn in Ihrem CodeBehind bereits eine Handlermethode mit der richtigen Signatur vorhanden ist, wird der Name dieser Methode zusammen mit der Option <Neuer Ereignishandler> in der Dropdownliste „AutoVervollständigen“ angezeigt. Sie können auch die Tabulatortaste als Verknüpfung drücken, anstatt auf die IntelliSense-Listenelemente zu klicken.
Definieren eines Ereignishandlers
Für Objekte, die UI-Elemente darstellen und in XAML deklariert werden, wird Ereignishandlercode in der partiellen Klasse definiert, die als CodeBehind für eine XAML-Seite fungiert. Ereignishandler sind Methoden, die Sie als Teil der Ihrem XAML zugeordneten partiellen Klasse schreiben. Diese Ereignishandler basieren auf den Delegaten, die ein bestimmtes Ereignis verwendet. Die Ereignishandlermethoden können öffentlich oder privat sein. Der private Zugriff funktioniert, weil der vom XAML erstellte Handler und die Instanz letztendlich durch die Codegenerierung verbunden werden. Im Allgemeinen wird empfohlen, die Ereignishandlermethoden in der Klasse öffentlich zu machen.
Hinweis: Ereignishandler für C++ werden nicht in partiellen Klassen definiert, sie werden im Header als privates Klassenmember deklariert. Bei den Erstellungsaktionen für ein C++-Projekt wird Code generiert, der das XAML-Typsystem und das CodeBehind-Modell für C++ unterstützt.
Der sender-Parameter und Ereignisdaten
Der Handler, den Sie für das Ereignis schreiben, kann auf zwei Werte zugreifen. Diese Werte sind jedes Mal als Eingabe verfügbar, wenn der Handler aufgerufen wird. Der erste Parameter ist sender. Dabei handelt es sich um einen Verweis auf das Objekt, dem der Handler angefügt ist. Der sender-Parameter ist als Object-Basistyp typisiert. Häufig wird sender in einen präziseren Typ umgewandelt. Diese Maßnahme ist nützlich, wenn Sie beabsichtigen, den Zustand direkt für das sender-Objekt zu überprüfen oder zu ändern. Abhängig davon, wo der Handler angefügt wird, und von anderen Designmerkmalen wissen Sie bei Ihrem eigenen App-Design in der Regel, dass es sich um einen Typ handelt, in den sender sicher umgewandelt werden kann
Den zweiten Wert stellen Ereignisdaten dar. In der Regel sind diese in Syntaxdefinitionen als e-Parameter enthalten. Wenn Sie feststellen möchten, welche Eigenschaften für Ereignisdaten verfügbar sind, prüfen Sie den e-Parameter des Delegaten, der dem behandelten Ereignis zugewiesen ist. Verwenden Sie dann IntelliSense oder den Objektkatalog in Visual Studio. Sie können auch in der Referenzdokumentation von Windows Runtime nachschlagen. Sie können auch in der Referenzdokumentation von Windows-Runtime nachschlagen.
Bei einigen Ereignissen ist die Kenntnis der speziellen Eigenschaftswerte der Ereignisdaten so wichtig wie die Kenntnis, dass das Ereignis eingetreten ist. Dies gilt insbesondere für die Eingabeereignisse. Bei Zeigerereignissen kann die Position des Zeigers beim Auslösen des Ereignisses wichtig sein. Bei Tastaturereignissen wird durch alle möglichen Tastendrücke ein KeyDown- und KeyUp-ausgelöst. Um zu bestimmen, welche Taste von einem Benutzer gedrückt wurde, müssen Sie auf die KeyRoutedEventArgs-Klasse zugreifen, die für den Ereignishandler verfügbar ist. Weitere Informationen zur Behandlung von Eingabeereignissen finden Sie unter Behandeln von Zeigereingaben und Tastaturinteraktionen. Bei Eingabeereignissen und Eingabeszenarien gibt es häufig weitere Überlegungen, die in diesem Thema nicht behandelt werden, wie das Erfassen des Zeigers bei Zeigerereignissen oder Zusatztasten und Plattformtastencodes für Tastaturereignisse.
Ereignishandler, die das async-Muster verwenden
In einigen Fällen wird die Verwendung von APIs empfohlen, die innerhalb eines Ereignishandlers ein async-Muster verwenden. Beispielsweise können Sie einen Button in einer AppBar verwenden, um eine Dateiauswahl anzuzeigen und mit dieser zu interagieren. Viele dieser Dateiauswahl-APIs sind jedoch asynchron. Sie müssen innerhalb eines async/awaitable-Bereichs aufgerufen werden. Dies wird vom Compiler erzwungen. Sie können also dem Ereignishandler das asynchrone Schlüsselwort hinzufügen, sodass der Handler jetzt asynchron leer ist. Nun kann der Ereignishandler async/awaitable-Aufrufe ausführen.
Ein Beispiel für die Benutzerinteraktions-Ereignisbehandlung mit dem async-Muster finden Sie unter Dateizugriff und -auswahl (in der Reihe Erstellen Ihrer ersten Windows-Runtime-App mit C# oder Visual Basic). Siehe auch [Aufrufen asynchroner APIs in C].
Hinzufügen von Ereignishandlern im Code
Es gibt noch andere Möglichkeiten als XAML, um einem Objekt einen Ereignishandler zuzuweisen. Sie können die spezielle Syntax zum Hinzufügen von Ereignishandlern einer Sprache nutzen, um bestimmten Objekten (auch Objekten, die in XAML nicht verwendet werden können) Ereignishandler im Code hinzufügen.
In C# wird für die Syntax der +=
-Operator verwendet. Sie registrieren den Handler, indem Sie auf der rechten Seite des Operators auf den Namen der Ereignishandlermethode verweisen.
Wenn Objekten, die in der Laufzeit-UI angezeigt werden, Ereignishandler per Code hinzugefügt werden, ist es üblich, diese Handler als Reaktion auf ein Objektlebensdauer-Ereignis oder einen Rückruf hinzuzufügen, wie z. B. Loaded oder OnApplyTemplate. Die Ereignishandler für das relevante Objekt stehen dann zur Laufzeit für Ereignisse bereit, die von Benutzern eingeleitet werden. Dieses Beispiel zeigt eine XAML-Gliederung der Seitenstruktur und führt anschließend die C#-Sprachsyntax für das Hinzufügen eines Ereignishandlers zu einem Objekt auf.
<Grid x:Name="LayoutRoot" Loaded="LayoutRoot_Loaded">
<StackPanel>
<TextBlock Name="textBlock1">Put the pointer over this text</TextBlock>
...
</StackPanel>
</Grid>
void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
textBlock1.PointerEntered += textBlock1_PointerEntered;
textBlock1.PointerExited += textBlock1_PointerExited;
}
Hinweis: Eine ausführlichere Syntax ist vorhanden. 2005 wurde in C# der Rückschluss auf Delegaten als neues Feature eingeführt. Damit kann der Compiler die neue Delegatinstanz ableiten, und die vorherige, einfachere Syntax kann verwendet werden. Die Funktion der ausführlichen Syntax entspricht dem vorherigen Beispiel. Vor der Registrierung wird jedoch explizit eine neue Delegatinstanz erstellt. Der Delegatrückschluss wird somit nicht genutzt. Diese explizite Syntax ist zwar nicht so üblich, aber dennoch in einigen Codebeispielen anzutreffen.
void LayoutRoot_Loaded(object sender, RoutedEventArgs e)
{
textBlock1.PointerEntered += new PointerEventHandler(textBlock1_PointerEntered);
textBlock1.PointerExited += new MouseEventHandler(textBlock1_PointerExited);
}
Für Visual Basic-Syntax gibt es zwei Möglichkeiten. Sie können eine parallele C#-Syntax verwenden und den Instanzen die Handler direkt anfügen. Dazu benötigen Sie das AddHandler-Schlüsselwort und den AddressOf-Operator, der den Handlermethodennamen dereferenziert.
Die zweite Möglichkeit für die Visual Basic-Syntax besteht darin, das Handles-Schlüsselwort für Ereignishandler zu verwenden. Das ist in Fällen angebracht, in denen beim Laden voraussichtlich Handler für Objekte vorhanden sind und für die Objektlebensdauer bestehen bleiben. Wenn Sie Handles für ein in XAML definiertes Objekt verwenden, müssen Sie einen Name / x:Name bereitstellen. Dieser Name wird zum Instanzenqualifizierer, der für den Instance.Event-Teil der Handles-Syntax benötigt wird. In diesem Fall benötigen Sie keinen Ereignishandler, der auf der Objektlebensdauer basiert, um das Anfügen der anderen Ereignishandler einzuleiten. Die Handles-Verbindungen werden beim Kompilieren der XAML-Seite erstellt.
Private Sub textBlock1_PointerEntered(ByVal sender As Object, ByVal e As PointerRoutedEventArgs) Handles textBlock1.PointerEntered
' ...
End Sub
Hinweis: Visual Studio und seine XAML-Entwurfsoberfläche fördern im Allgemeinen die instance-Handling-Technik anstelle des Handles-Schlüsselworts. Das Erstellen der Ereignishandlerverknüpfung in XAML ist Teil eines typischen Designer-Entwickler-Workflows, und die Handles-Schlüsselworttechnik ist mit dem Verknüpfen der Ereignishandler in XAML nicht kompatibel.
In C++/CX verwenden Sie auch die += Syntax, aber es gibt Unterschiede zum grundlegenden C#-Formular:
- Es gibt keinen Rückschluss auf Delegaten. Sie müssen deshalb ref new für die Delegatinstanz verwenden.
- Der Delegatkonstruktor besitzt zwei Parameter und benötigt das Zielobjekt als ersten Parameter. Normalerweise geben Sie this an.
- Der Delegatenkonstruktor erfordert die Methodenadresse als zweiten Parameter, sodass der &-Verweisoperator vor dem Methodennamen steht.
textBlock1().PointerEntered({this, &MainPage::TextBlock1_PointerEntered });
textBlock1->PointerEntered +=
ref new PointerEventHandler(this, &BlankPage::textBlock1_PointerEntered);
Entfernen von Ereignishandlern im Code
Das Entfernen von Ereignishandlern im Code ist in der Regel nicht erforderlich, auch wenn sie von Ihnen im Code hinzugefügt wurden. Das Verhalten der Objektlebensdauer für die meisten Windows-Runtime-Objekte, z. B. Seiten und Steuerelemente, vernichtet die Objekte, wenn die Verbindung mit dem Haupt-Fenster und der dazugehörigen visuellen Struktur getrennt wird. Vorhandene Delegatverweise werden ebenfalls vernichtet. .NET führt dies durch die automatische Speicherbereinigung durch, und Windows-Runtime mit C++/CX verwendet standardmäßig schwache Verweise.
In einigen seltenen Fällen sollten Ereignishandler explizit entfernt werden. Dazu zählen unter anderem folgende Einstellungen:
- Für statische Ereignisse hinzugefügte Handler, für die keine konventionelle Garbage Collection durchgeführt werden kann. Die Ereignisse der Klassen CompositionTarget und Clipboard sind Beispiele für statische Ereignisse in der Windows-Runtime-API.
- Testen Sie Code, wenn Sie Handler sofort entfernen möchten, oder Code, in dem alte und neue Ereignishandler für ein Ereignis zur Laufzeit ausgetauscht werden sollen.
- Die Implementierung einer benutzerdefinierten Remove-Zugriffsmethode.
- Benutzerdefinierte statische Ereignisse.
- Handler für Seitennavigationen.
FrameworkElement.Unloaded oder Page.NavigatedFrom sind mögliche Ereignisauslöser mit entsprechenden Positionen in der Zustandsverwaltung und der Objektlebensdauer, sodass sie zum Entfernen von Handlern für andere Ereignisse verwendet werden können.
Beispielsweise können Sie mithilfe dieses Codes einen Ereignishandler namens textBlock1_PointerEntered aus dem Zielobjekt textBlock1 entfernen.
textBlock1.PointerEntered -= textBlock1_PointerEntered;
RemoveHandler textBlock1.PointerEntered, AddressOf textBlock1_PointerEntered
Sie können Handler auch in solchen Fällen entfernen, in denen das Ereignis über ein XAML-Attribut hinzugefügt wurde, was bedeutet, dass der Handler in generiertem Code hinzugefügt wurde. Dies ist einfacher, wenn Sie einen Name-Wert für das Element bereitstellen, dem der Handler angehängt wurde, da hierdurch später ein Objektverweis für den Code bereitgestellt wird. Sie können jedoch auch in der Objektstruktur nach dem erforderlichen Objekt suchen, wenn kein Name für das Objekt vorhanden ist.
Wenn Sie einen Ereignishandler in C++/CX entfernen müssen, benötigen Sie ein Registrierungstoken, das Sie im Rückgabewert der +=
-Ereignishandlerregistrierung erhalten haben sollten. Der Grund ist, dass es sich bei dem Wert, den Sie für die rechte Seite der Aufhebung der -=
-Registrierung in C++/CX-Syntax verwenden, um das Token handelt, nicht beim Methodennamen. Für C++/CX können Sie als XAML-Attribut hinzugefügte Handler nicht entfernen, da der generierte C++/CX-Code kein Token speichert.
Routingereignisse
Die Windows-Runtime mit C#, Microsoft Visual Basic oder C++/CX unterstützt das Konzept von Routingereignissen für eine Gruppe von Ereignissen, die für die meisten UI-Elementen verwendet werden. Diese Ereignisse werden für Eingabe- und Benutzerinteraktionsszenarien verwendet und in der UIElement-Basisklasse implementiert. Die folgenden Eingabeereignisse sind Routingereignisse:
- BringIntoViewRequested
- CharacterReceived
- ContextCanceled
- ContextRequested
- DoubleTapped
- DragEnter
- DragLeave
- DragOver
- DragStarting
- Drop
- DropCompleted
- GettingFocus
- GotFocus
- Holding
- KeyDown
- KeyUp
- LosingFocus
- LostFocus
- ManipulationCompleted
- ManipulationDelta
- ManipulationInertiaStarting
- ManipulationStarted
- ManipulationStarting
- NoFocusCandidateFound
- PointerCanceled
- PointerCaptureLost
- PointerEntered
- PointerExited
- ZeigerMoved
- PointerPressed
- PointerReleased
- PointerWheelChanged
- Previewkeydown
- PreviewKeyUp
- PointerWheelChanged
- RightTapped
- Tapped
Ein Routingereignis ist ein Ereignis, das möglicherweise von einem untergeordneten Objekt an jedes seiner übergeordneten Objekte in der Objektstruktur weitergeleitet (geroutet) wird. Die XAML-Struktur Ihrer UI kommt dieser Struktur nahe, wobei der Stamm der Struktur dem Stammelement im XAML entspricht. Die tatsächliche Objektstruktur kann sich von der XAML-Elementschachtelung unterscheiden, da die Objektstruktur keine XAML-Sprachfeatures wie Eigenschaftenelementtags enthält. Routingereignisse durchlaufen die Struktur nach dem Bubblingkonzept von untergeordneten XAML-Objektelementen, die das Ereignis auslösen, zu den übergeordneten Objekten, in denen sie enthalten sind. Das Ereignis und seine Ereignisdaten können für mehrere Objekte entlang der Ereignisroute behandelt werden. Wenn kein Element über Handler verfügt, wird die Route u. U. bis zum Stammelement fortgesetzt.
Wenn Sie Webtechnologien wie Dynamic HTML (DHTML) oder HTML5 kennen, sind Sie möglicherweise bereits mit dem Bubbling-Ereigniskonzept vertraut.
Wenn für ein Routingereignis ein Bubbling durch die zugehörige Ereignisroute erfolgt, greifen alle angefügten Ereignishandler auf eine freigegebene Instanz von Ereignisdaten zu. Deshalb werden Änderungen an Ereignisdaten an den nächsten Handler weitergegeben, wenn einer der Handler Schreibzugriff auf Ereignisdaten besitzt. Die Daten entsprechen dann möglicherweise nicht mehr den ursprünglichen Ereignisdaten. Wenn ein Ereignis ein Routingereignisverhalten besitzt, wird in der Referenzdokumentation entsprechend darauf hingewiesen.
Die OriginalSource-Eigenschaft von RoutedEventArgs
Wenn ein Ereignis entlang einer Route ein Bubbling durchläuft, istsender nicht mehr das gleiche Objekt, das das Ereignis ausgelöst hat. Stattdessen ist sender das Objekt, an das der aufgerufene Handler angefügt ist.
In einigen Fällen interessieren Sie sich nicht für das sender-Objekt, sondern eher dafür, auf welchem der möglichen untergeordneten Objekte sich der Mauszeiger beim Auslösen eines Zeigerereignisses befindet oder welches Objekt in einer größeren UI beim Drücken einer Taste den Fokus hatte. In diesen Fällen können Sie den Wert der OriginalSource-Eigenschaft verwenden. An allen Punkten auf der Route meldet OriginalSource das ursprüngliche Objekt, von dem das Ereignis ausgelöst wurde, und nicht das Objekt, dem der Handler angefügt ist. Bei UIElement-Eingabeereignissen ist dieses ursprüngliche Objekt jedoch häufig ein Objekt, das im-XAML-Code der UI-Definition auf Seitenebene nicht sofort sichtbar ist. Stattdessen kann es sich bei diesem ursprünglichen Quellobjekt um einen vorlagenbasierten Teil eines Steuerelements handeln. Wenn der Benutzer beispielsweise den Mauszeiger über den Rand einer Schaltfläche bewegt, ist die OriginalSource bei den meisten Zeigerereignissen einen Rahmenvorlagenpart in der Vorlage, nicht um die Schaltfläche selbst.
Tipp: Das Bubbling von Eingabeereignissen ist besonders nützlich, wenn Sie ein Steuerelement mit Vorlagen erstellen. Auf jedes Steuerelement, das eine Vorlage besitzt, kann durch seinen Consumer eine neue Vorlage angewendet werden. Der Consumer, der eine Arbeitsvorlage erneut zu erstellen versucht, kann versehentlich in der Standardvorlage deklarierten Ereignisbehandlungscode entfernen. Sie können dennoch eine Ereignisbehandlung auf Steuerelementebene bereitstellen, indem Sie Handler als Teil OnApplyTemplate-Überschreibung in der Klassendefinition anfügen. Anschließend können Sie die Eingabeereignisse abfangen, die bei der Instanziierung per Bubbling zum Stamm des Steuerelements weitergeleitet werden.
Die Handled-Eigenschaft
Einige Ereignisdatenklassen für bestimmte Routingereignisse enthalten eine Eigenschaft mit dem Namen Handled. Beispiele finden Sie unter PointerRoutedEventArgs.Handled, KeyRoutedEventArgs.Handled und DragEventArgs.Handled. In allen Fällen ist Handled eine festlegbare boolesche Eigenschaft.
Wenn die Eigenschaft Handled auf true festgelegt wird, wirkt sich dies auf das Ereignissystemverhalten aus. Wenn Handled auf true festgelegt wurde, wird das Routing für die meisten Ereignishandler beendet. Das Ereignis wird nicht über die Route weitergeleitet, um andere angefügte Handler über dieses spezielle Ereignis zu informieren. Was „behandelt“ im Kontext des Ereignisses bedeutet und wie Ihre App auf ein behandeltes Ereignis reagiert, liegt in Ihrem Ermessen. Im Grunde ist Handled ein einfaches Protokoll, mit dem im App-Code angegeben werden kann, dass das Auftreten eines Ereignisses nicht per Bubbling an Container weitergeleitet werden muss, da Ihre App-Logik sich um die notwendigen Schritte gekümmert hat. Umgekehrt müssen Sie jedoch darauf achten, dass Sie keine Ereignisse behandeln, die wahrscheinlich ein Bubbling ausführen sollten, damit integrierte System- oder Steuerungsverhalten reagieren können. Beispielsweise kann die Behandlung von Ereignissen auf niedriger Ebene in den Teilen oder Elementen eines Auswahlsteuerelements nachteilig sein. Das Auswahlsteuerelement sucht möglicherweise nach Eingabeereignissen, um festzustellen, ob die Auswahl geändert werden muss.
Nicht alle Routingereignisse können eine Route auf diese Weise abbrechen, da sie nicht über die Eigenschaft Handled verfügen. Beispielsweise führen GotFocus and LostFocus Bubbling aus. Sie führen dieses Bubbling jedoch stets bis zum Stamm aus, und ihre Ereignisdatenklassen besitzen die Eigenschaft Handled nicht, die dieses Verhalten beeinflussen kann.
Eingabeereignishandler in Steuerelementen
Bei bestimmten Windows-Runtime-Steuerelementen wird das Handled-Konzept manchmal intern für Eingabeereignisse verwendet. Dadurch kann der Anschein erweckt werden, dass niemals ein Eingabeereignis eintritt, weil es vom Benutzercode nicht verarbeitet werden kann. Beispielsweise enthält die Button-Klasse Logik, die das allgemeine Eingabeereignis PointerPressed bewusst behandelt. Der Grund hierfür ist, dass Schaltflächen ein Click-Ereignis auslösen, das durch Eingaben mit gedrücktem Zeiger sowie durch andere Eingabemodi initiiert wird (z. B. durch Tasten wie die EINGABETASTE, die die Schaltfläche aufrufen können, wenn sie den Fokus besitzt). Im Rahmen des Klassendesigns von Button wird das Rohdateneingabe-Ereignis konzeptionell behandelt. Klassenconsumer wie Ihr Benutzercode können stattdessen mit dem steuerungsrelevanten Click-Ereignis interagieren. In den entsprechenden Themen zu den speziellen Steuerelementklassen in der API-Referenz für die Windows-Runtime wird häufig das von der Klasse implementierte Ereignisbehandlungsverhalten erwähnt. In einigen Fällen können Sie das Verhalten ändern, indem Sie OnEvent überschreiben. Sie können beispielsweise die Art ändern, wie Ihre von TextBox abgeleitete Klasse auf Tasteneingaben reagiert, indem Sie Control.OnKeyDown überschreiben.
Registrieren von Handlern für bereits behandelte Routingereignisse
Weiter oben wurde erwähnt, dass die meisten Handler nicht aufgerufen werden, wenn Handled auf true festgelegt ist. Die Methode AddHandler bietet jedoch eine Technik, um einen Handler anzufügen, der für die Route immer aufgerufen wird. Das geschieht selbst dann, wenn für andere Handler an früherer Stelle in der Route Handled auf true in den geteilten Ereignisdaten festgelegt wurde. Diese Technik ist nützlich, wenn ein von Ihnen verwendetes Steuerelement das Ereignis in seiner inneren Zusammensetzung behandelt hat. Sie eignet sich auch für spezielle Steuerelementlogik, bei der Sie weiterhin mit einer Steuerelementinstanz oder Ihrer App-UI auf das Ereignis reagieren möchten. Diese Technik sollte jedoch mit Bedacht eingesetzt werden, da sie dem Zweck von Handled widersprechen und die beabsichtigten Interaktionen des Steuerelements verhindern kann.
Nur Routingereignisse mit entsprechenden Routingereignisbezeichnern können die Technik zur AddHandler-Ereignisbehandlung nutzen, da der Bezeichner als Eingabe für die AddHandler-Methode erforderlich ist. In der Referenzdokumentation zu AddHandler finden Sie eine Liste der Ereignisse, für die Routingereignisbezeichner verfügbar sind. Diese Liste stimmt größtenteils mit der bereits gezeigten Liste mit Routingereignissen überein. Die letzten beiden Ereignisse in der Liste, GotFocus und LostFocus, stellen eine Ausnahme dar, da sie keinen Routingereignisbezeichner besitzen. Daher können Sie AddHandler nicht für diese Ereignisse verwenden.
Routingereignisse außerhalb der visuellen Struktur
Bestimmte Objekte sind Bestandteil einer Beziehung mit der primären visuellen Struktur. Diese ist konzeptionell, wie z. B. eine Überlagerung über die visuellen Hauptobjekte. Diese bestimmten Objekte gehören nicht zu den üblichen Beziehungen zwischen übergeordneten und untergeordneten Elementen, mit denen alle Strukturelemente mit dem visuellen Stamm verbunden sind. Das betrifft jedes angezeigte Popup- oder ToolTip-Element. Wenn Sie Routingereignisse eines Popup- oder ToolTip-Elements behandeln möchten, platzieren Sie die Handler für bestimmte UI-Elemente innerhalb des Popup- oder ToolTip-Elements und nicht direkt für die Popup- oder ToolTip-Elemente. Verlassen Sie sich nicht auf das Routing innerhalb einer Zusammensetzung, die für Popup- oder ToolTip-Inhalte ausgeführt wird. Das Ereignisrouting für geroutete Ereignisse funktioniert nur entlang der visuellen Hauptstruktur. Ein Popup- oder ToolTip-Element wird nicht als übergeordnetes Element untergeordneter UI-Elemente betrachtet und empfängt das Routingereignis in keinem Fall, auch wenn es versucht, Elemente wie den Popup-Standardhintergrund als Erfassungsbereich für Eingabeereignisse zu verwenden.
Treffertests und Eingabeereignisse
Das Bestimmen, ob und wo auf der UI ein Element für die Maus-, Touch und Stifteingabe sichtbar ist, wird als Treffertests bezeichnet. Bei Toucheingabeaktionen und interaktionsspezifischen Ereignissen oder Manipulationsereignissen, die aus einer Toucheingabeaktion resultieren, muss ein Element bei Treffertests sichtbar sein, damit es der Ereignisquelle entsprechen und das der Aktion zugeordnete Ereignis auslösen kann. Andernfalls durchläuft die Aktion das Element bis zu zugrunde liegenden oder übergeordneten Elementen in der visuellen Struktur, die mit dieser Eingabe interagieren könnte. Treffertests werden von mehreren Faktoren beeinflusst. Sie können jedoch feststellen, ob ein bestimmtes Element Eingabeereignisse auslösen kann, indem Sie die zugehörige Eigenschaft IsHitTestVisible überprüfen. Diese Eigenschaft gibt nur dann true zurück, wenn das Element die folgenden Kriterien erfüllt:
- Der Visibility-Eigenschaftenwert ist Visible.
- Der Wert der Eigenschaft Background oder Fill ist nicht null. Ein NULL-Pinselwert führt zu Transparenz und Treffertest in der Sichtbarkeit. (Wenn Sie ein Element transparent machen und zugleich Treffertests für das Element ermöglichen möchten, verwenden Sie einen Transparent-Pinsel anstelle von null.)
Hinweis: Background und Fill werden nicht durch UIElement definiert, sondern durch verschiedene abgeleitete Klassen wie Control und Shape. Die von Ihnen für Vorder- und Hintergrundeigenschaften verwendeten Implikationen von Pinseln sind jedoch für Treffertests und Eingabeereignisse identisch. Dabei ist es unerheblich, welche Unterklasse die Eigenschaften implementiert.
- Wenn das Element ein Steuerelement ist, muss dessen Wert für die Eigenschaft IsEnabled true sein.
- Das Element muss im Layout über reale Dimensionen verfügen. Ein Element, bei dem ActualHeight and ActualWidth 0 sind, kann keine Eingabeereignisse auslösen.
Bei einigen Steuerelementen sind besondere Regeln bezüglich Treffertests zu beachten. Beispielsweise verfügt TextBlock nicht über die Background-Eigenschaft. Dennoch können innerhalb des gesamten Bereichs der zugehörigen Dimensionen Treffertests ausgeführt werden. Image- und MediaElement-Steuerelemente können in den zugehörigen definierten Rechtecksdimensionen auf Treffer getestet werden. Dabei ist es unerheblich, ob in der Medienquelldatei transparente Inhalte wie Alphakanäle angezeigt werden. WebView-Steuerelemente weisen ein spezielles Treffertestverhalten auf, da die Eingabe durch das gehostete HTML behandelt werden kann und Skriptereignisse auslösen kann.
Für die meisten Panel-Klassen und Border-Elemente können im eigenen Hintergrund zwar keine Treffertests ausgeführt werden, sie können jedoch dennoch Benutzereingabeereignisse verarbeiten, die von den in ihnen integrierten Elementen weitergeleitet werden.
Elemente, die sich an der gleichen Position wie ein Benutzereingabeereignis befinden, können unabhängig davon ermittelt werden, ob sie für Treffertests infrage kommen. Rufen Sie dazu die FindElementsInHostCoordinates-Methode auf. Wie der Name nahe legt, findet diese Methode die Elemente an einer Position relativ zu einem angegebenen Hostelement. Transformationen und Layoutänderungen können sich jedoch auf das Koordinatensystem eines Elements auswirken und somit die an einer bestimmten Position gefundenen Elemente beeinflussen.
Befehle
Eine kleine Anzahl von UI-Elementen unterstützt Befehle. Befehle verwenden eingabebezogene Routingereignisse in der zugrunde liegenden Implementierung und ermöglichen die Verarbeitung verwandter UI-Eingaben (eine bestimmte Zeigeraktion, eine bestimmte Tastenkombination) durch Aufrufen eines einzelnen Befehlshandlers. Wenn die Befehle für ein UI-Element verfügbar sind, sollten Sie die Befehls-APIs anstelle einzelner Eingabeereignisse verwenden. In der Regel verwenden Sie einen Bindungsverweis in Eigenschaften einer Klasse, die das Ansichtsmodell für Daten definiert. Die Eigenschaften enthalten benannte Befehle, die das sprachspezifische ICommand-Befehlsmuster implementieren. Weitere Informationen finden Sie unter ButtonBase.Command.
Benutzerdefinierte Ereignisse in der Windows-Runtime
Zum Definieren von benutzerdefinierten Ereignissen hängt die Art des Hinzufügens des Ereignisses und dessen Bedeutung für das Klassendesign stark von der verwendeten Programmiersprache ab.
- Für C# und Visual Basic definieren Sie ein CLR-Ereignis. Sie können das standardmäßige .NET-Ereignismuster verwenden, solange Sie keine benutzerdefinierten Zugriffsmethoden verwenden (add/remove). Weitere Tipps:
- Für den Ereignishandler ist es ratsam,System.EventHandler<TEventArgs> zu verwenden, da er eine integrierte Übersetzung in den generischen Ereignisdelegat EventHandler<T> von Windows-Runtime enthält.
- Basieren Sie Ihre Ereignisdatenklasse nicht auf System.EventArgs, da sie nicht in Windows-Runtime übersetzt wird. Verwenden Sie eine vorhandene Ereignisdatenklasse oder gar keine Basisklasse.
- Wenn Sie benutzerdefinierte Zugriffstasten verwenden, lesen Sie benutzerdefinierte Ereignisse und Ereigniszugriffstasten in Windows-Runtime- Komponenten.
- Wenn Sie nicht genau wissen, was das standardmäßige .NET-Ereignismuster ist, lesen Sie Definition von Ereignissen für benutzerdefinierte Silverlight-Klassen. Dies wurde für Microsoft Silverlight geschrieben, der Code und die Konzepte bieten sich aber auch für das standardmäßige .NET-Ereignismuster an.
- Informationen zu C++/CX finden Sie unter Ereignisse (C++/CX)
- Verwenden Sie benannte Verweise auch für ihre eigenen Verwendungen von benutzerdefinierten Ereignissen. Verwenden Sie lambda nicht für benutzerdefinierte Ereignisse; dadurch kann einen Zirkelverweis erstellt werden.
Sie können kein benutzerdefiniertes Routingereignis für Windows-Runtime deklarieren. Routingereignisse sind auf den Satz beschränkt, der aus der Windows-Runtime stammt.
Das Definieren eines benutzerdefinierten Ereignisses erfolgt in der Regel als Teil der Übung zum Definieren eines benutzerdefinierten Steuerelements. Eine Abhängigkeitseigenschaft mit einem Rückruf für Eigenschaftsänderungen ist ein gängiges Muster, genau wie das Definieren eines benutzerdefinierten Ereignisses, das in manchen oder in allen Fällen durch den Abhängigkeitseigenschaftsrückruf ausgelöst wird. Benutzer Ihres Steuerelements haben keinen Zugriff auf den von Ihnen definierten Rückruf für Eigenschaftsänderungen; die beste Methode ist das Bereitstellen eines Benachrichtigungsereignisses. Weitere Informationen finden Sie unter Benutzerdefinierte Abhängigkeitseigenschaften.