Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Handlers voor een gerouteerde gebeurtenis kunnen de gebeurtenis als afgehandeld markeren binnen de gebeurtenisgegevens. Het afhandelen van de gebeurtenis verkort de route effectief. Klasseafhandeling is een programmeerconcept dat wordt ondersteund door gerouteerde gebeurtenissen. Een klasse-handler heeft de mogelijkheid om een bepaalde gerouteerde gebeurtenis op klasseniveau te verwerken met een handler die wordt aangeroepen vóór een instantie-handler op elke instantie van de klasse.
Voorwaarden
In dit onderwerp worden de concepten beschreven die zijn geïntroduceerd in het Overzicht van gerouteerde gebeurtenissen.
Wanneer gebeurtenissen als afgehandeld markeren
Wanneer u de waarde van de eigenschap Handled instelt op true
in de gebeurtenisgegevens voor een gerouteerde gebeurtenis, wordt dit aangeduid als 'het markeren van de gebeurtenis die is verwerkt'. Er is geen absolute regel voor wanneer u gerouteerde gebeurtenissen moet markeren als verwerkt, hetzij als auteur van een toepassing of als besturingselementauteur die reageert op bestaande gerouteerde gebeurtenissen of nieuwe gerouteerde gebeurtenissen implementeert. Voor het grootste deel moet het concept 'afgehandeld', zoals toegepast in de gebeurtenisgegevens van de gerouteerde gebeurtenis, worden gebruikt als een specifiek protocol voor de reacties van uw eigen toepassing op de verschillende gerouteerde gebeurtenissen die in WPF-API's beschikbaar zijn, evenals voor eventuele aangepaste gerouteerde gebeurtenissen. Een andere manier om rekening te houden met het probleem 'afgehandeld', is dat u over het algemeen een gerouteerde gebeurtenis moet markeren die wordt afgehandeld als uw code op een significante en relatief volledige manier op de gerouteerde gebeurtenis heeft gereageerd. Normaal gesproken mag er niet meer dan één significant antwoord nodig zijn dat aparte handler-implementaties vereist voor een enkele gerouteerde gebeurtenis. Als er meer antwoorden nodig zijn, moet de benodigde code worden geïmplementeerd via toepassingslogica die is gekoppeld binnen één handler in plaats van door het gerouteerde gebeurtenissysteem te gebruiken voor doorsturen. Het concept van wat 'significant' is, is ook subjectief en is afhankelijk van uw toepassing of code. Enkele voorbeelden van een 'significante respons' zijn onder andere: het instellen van de focus, het wijzigen van de publieke status, het instellen van eigenschappen die de visuele weergave beïnvloeden, en het uitlokken van andere nieuwe gebeurtenissen. Voorbeelden van niet-belangrijke antwoorden zijn: het wijzigen van de privéstatus (zonder visuele impact of programmatische weergave), logboekregistratie van gebeurtenissen of het bekijken van argumenten van een gebeurtenis en het kiezen om er niet op te reageren.
Het gedrag van het gerouteerde gebeurtenissysteem versterkt dit 'significante antwoord'-model voor het gebruik van de afgehandelde status van een gerouteerde gebeurtenis, omdat handlers die zijn toegevoegd in XAML of de algemene handtekening van AddHandler niet worden aangeroepen als reactie op een gerouteerde gebeurtenis waarbij de gebeurtenisgegevens al zijn gemarkeerd als afgehandeld. U moet de extra moeite doen om een handler toe te voegen met de handledEventsToo
parameterversie (AddHandler(RoutedEvent, Delegate, Boolean)) om gerouteerde gebeurtenissen te verwerken die als afgehandeld zijn gemarkeerd door eerdere deelnemers aan de gebeurtenisroute.
In sommige gevallen markeren de bedieningsknoppen zelf bepaalde gerouteerde gebeurtenissen als verwerkt. Een afgehandelde gerouteerde gebeurtenis vertegenwoordigt een beslissing van WPF-besturingsauteurs dat de acties van het besturingselement als reactie op de gerouteerde gebeurtenis aanzienlijk of volledig zijn als onderdeel van de implementatie van de controle en dat de gebeurtenis geen verdere verwerking nodig heeft. Dit wordt meestal gedaan door een klasse-handler toe te voegen voor een event, of door een van de virtuele methoden van de klasse-handler te overschrijven die op een basisklasse bestaan. U kunt deze gebeurtenisafhandeling nog steeds omzeilen indien nodig; zie Werken rond gebeurtenisonderdrukking door besturingselementen verderop in dit onderwerp.
"Preview" (Tunneling)-gebeurtenissen versus Bubbling-gebeurtenissen en gebeurtenisafhandeling
Voorbeeld van gerouteerde gebeurtenissen zijn gebeurtenissen die een tunnelroute volgen via de elementstructuur. De "Preview", zoals uitgedrukt in de naamgevingsconventie, geeft het algemene principe aan voor invoergebeurtenissen waarbij previewgebeurtenissen (tunneling) worden geactiveerd vóór de overeenkomstige bubbelende gebeurtenis. Invoergerouteerde gebeurtenissen met een tunneling- en bubblingpaar hebben ook een afzonderlijke verwerkingslogica. Als de tunneling/preview gerouteerde gebeurtenis gemarkeerd is als afgehandeld door een gebeurtenislistener, dan wordt ook de bubbelende gerouteerde gebeurtenis als afgehandeld gemarkeerd, zelfs voordat enige listeners van de bubbelende gerouteerde gebeurtenis deze ontvangen. De tunneling- en bubbling-gerouteerde gebeurtenissen zijn technisch gezien aparte gebeurtenissen, maar ze delen opzettelijk dezelfde instantie van gebeurtenisgegevens om dit gedrag mogelijk te maken.
De verbinding tussen de tunneling- en bubbelende gerouteerde gebeurtenissen wordt bereikt door de interne manier waarop een bepaalde WPF-klasse zijn eigen gedeclareerde gerouteerde gebeurtenissen oproept, wat ook geldt voor de gekoppelde invoergebeurtenissen. Maar tenzij deze implementatie op klasseniveau bestaat, is er geen verband tussen een tunneling-gerouteerde gebeurtenis en een bubbling-gerouteerde gebeurtenis die het naamgevingsschema delen: zonder zo'n implementatie zouden ze twee volledig afzonderlijke gerouteerde gebeurtenissen zijn en zouden ze niet opeenvolgend worden opgeroepen of gebeurtenisgegevens delen.
Zie voor meer informatie over het implementeren van tunnel-/belleninvoer-gebeurtenisparen in een aangepaste klasse, Een aangepaste gerouteerde gebeurtenismaken.
Klassehandlers en instantiehandlers
Gerouteerde gebeurtenissen beschouwen twee verschillende soorten listeners voor de gebeurtenis: klasselisteners en instantielisteners. Klassenlisteners bestaan omdat typen een bepaalde EventManager-API,RegisterClassHandler, in hun statische constructor hebben genoemd of een virtuele klassehandlermethode van een elementbasisklasse hebben overschreven. Exemplaarlisteners zijn specifieke instantieklassen of elementen waarbij een of meer handlers zijn gekoppeld aan die gerouteerde gebeurtenis via een aanroep naar AddHandler. Bestaande WPF-gerouteerde gebeurtenissen maken aanroepen naar AddHandler als onderdeel van de CLR-gebeurteniswikkelaar (Common Language Runtime) voegen{} toe en verwijderen{} implementaties van de gebeurtenis, wat ook de manier is waarop het eenvoudige XAML-mechanisme voor het koppelen van gebeurtenis-handlers via een kenmerksyntaxis wordt ingeschakeld. Daarom is zelfs het eenvoudige XAML-gebruik uiteindelijk gelijk aan een AddHandler-aanroep.
Elementen in de visualstructuur worden gecontroleerd op geregistreerde handler-implementaties. Handlers worden mogelijk aangeroepen tijdens de route, in de volgorde die inherent is aan het type routeringsstrategie voor die gerouteerde gebeurtenis. Bubbelende gerouteerde gebeurtenissen roepen bijvoorbeeld eerst die handlers aan die zijn gekoppeld aan hetzelfde element dat de gerouteerde gebeurtenis heeft veroorzaakt. Vervolgens borrelt de gerouteerde gebeurtenis naar het volgende bovenliggende element, totdat het hoofdelement van de toepassing is bereikt.
Vanuit het perspectief van het rootelement in een bubblingroute, als klasseafhandeling of een element dichter bij de bron van de gerouteerde gebeurtenis handlers aanroept die de gebeurtenisargumenten markeren als afgehandeld, worden handlers op het rootelement niet aangeroepen en wordt de gebeurtenisroute effectief ingekort voordat dat rootelement wordt bereikt. De route wordt echter niet volledig onderbroken, omdat handlers kunnen worden toegevoegd waarbij een speciale voorwaarde wordt gebruikt zodat ze toch aangeroepen moeten worden, zelfs als een klassebehandelaar of instantiebehandelaar de gerouteerde gebeurtenis als verwerkt heeft gemarkeerd. Dit wordt uitgelegd in Instance handlers toevoegen die worden aangeroepen, zelfs wanneer gebeurtenissen als afgehandeld worden gemarkeerd, verderop in dit onderwerp.
Op een dieper niveau dan de gebeurtenisroute zijn er ook mogelijk meerdere klassehandlers die invloed uitoefenen op een bepaalde instantie van een klasse. Dit komt doordat het klasseafhandelingsmodel voor gerouteerde gebeurtenissen alle mogelijke klassen in een klassehiërarchie mogelijk maakt om elke klasse-handler voor elke gerouteerde gebeurtenis te registreren. Elke klasse-handler wordt toegevoegd aan een intern archief en wanneer de gebeurtenisroute voor een toepassing wordt samengesteld, worden de klasse-handlers allemaal toegevoegd aan de gebeurtenisroute. Klasse-handlers worden toegevoegd aan de route, zodat de meest afgeleide klassehandler eerst wordt aangeroepen en klasse-handlers van elke volgende basisklasse worden aangeroepen. Over het algemeen worden klassehandlers niet zodanig geregistreerd dat ze reageren op gerouteerde gebeurtenissen die al als afgehandeld zijn gemarkeerd. Daarom maakt dit mechanisme voor klasseafhandeling een van de volgende twee opties mogelijk:
Afgeleide klassen kunnen de verwerking van klassegebeurtenissen die van de basisklasse zijn overgenomen, aanvullen door een handler toe te voegen die de gerouteerde gebeurtenis niet als verwerkt markeert, aangezien de handler van de basisklasse pas na de handler van de afgeleide klasse zal worden aangeroepen.
Afgeleide klassen kunnen de klasseafhandeling van de basisklasse vervangen door een klasse-handler toe te voegen die de gerouteerde gebeurtenis markeert. U moet voorzichtig zijn met deze aanpak, omdat het mogelijk het beoogde basisbesturingselementontwerp wijzigt in gebieden zoals visuele vormgeving, statuslogica, invoerafhandeling en opdrachtafhandeling.
Klasseafhandeling van gerouteerde gebeurtenissen op basisklassen van besturingselementen
Op elk elementknooppunt in een eventroute hebben klasse-gebeurtenisluisteraars de mogelijkheid om te reageren op het gerouteerde event voordat een instantie-gebeurtenisluisteraar op het element kan reageren. Daarom worden klasse-handlers soms gebruikt om gerouteerde gebeurtenissen te onderdrukken die een bepaalde controleklasse-implementatie niet verder wil doorgeven of om speciale verwerking van die gerouteerde gebeurtenis te bieden die een functie van de klasse is. Een klasse kan bijvoorbeeld een eigen klassespecifieke gebeurtenis genereren die specifiekere informatie bevat over wat sommige gebruikersinvoervoorwaarde betekent in de context van die specifieke klasse. De klasse-implementatie kan vervolgens de meer algemene gerouteerde gebeurtenis als afgehandeld markeren. Klassehandlers worden meestal toegevoegd zodat ze niet aangeroepen worden voor gerouteerde gebeurtenissen wanneer de gedeelde gebeurtenisgegevens al als afgehandeld zijn gemarkeerd. Maar voor uitzonderlijke gevallen bestaat er ook een RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean)-signatuur waarmee klassehandlers worden geregistreerd om aan te roepen, zelfs wanneer de gerouteerde gebeurtenissen al als afgehandeld zijn gemarkeerd.
Class Handler Virtuals
Sommige elementen, met name de basiselementen zoals UIElement, bevatten lege virtuele methoden 'On*Event' en 'OnPreview*Event' die overeenkomen met hun lijst met openbare gerouteerde gebeurtenissen. Deze virtuele methoden kunnen worden overschreven om een klassehandler te implementeren voor dat gerouteerde evenement. De basiselementklassen registreren deze virtuele methoden als hun klasse-handler voor elke dergelijke gerouteerde gebeurtenis met behulp van RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) zoals eerder beschreven. Met de virtuele methoden On*Event is het veel eenvoudiger om klasseafhandeling te implementeren voor de relevante gerouteerde gebeurtenissen, zonder dat voor elk type speciale initialisatie in statische constructors is vereist. U kunt bijvoorbeeld klasseafhandeling toevoegen voor de gebeurtenis DragEnter in elke UIElement afgeleide klasse door de OnDragEnter virtuele methode te overschrijven. Binnen de overschrijving kun je de gerouteerde gebeurtenis afhandelen, andere gebeurtenissen genereren, klassespecifieke logica initiëren die mogelijk de eigenschappen van elementen op exemplaren veranderen, of een combinatie van deze acties uitvoeren. In het algemeen moet u de basisimplementatie in dergelijke overrides aanroepen, zelfs als u het evenement als verwerkt markeert. Het aanroepen van de basis-implementatie wordt sterk aanbevolen omdat de virtuele methode zich op de basisklasse bevindt. Het standaard beveiligde virtuele patroon waarbij de basisimplementaties vanuit elke virtuele worden aangeroepen, vervangt en functioneert naast een vergelijkbaar mechanisme dat inherent is aan het afhandelen van gerouteerde gebeurtenisklassen. Hierbij worden klassehandlers voor alle klassen in een klassehiërarchie aangeroepen op een specifieke instantie, beginnend met de handler van de meest afgeleide klasse en doorlopend naar de handler van de basisklasse. U moet alleen de aanroep van de basis-implementatie weglaten als uw klasse een opzettelijke vereiste heeft om de logica voor de verwerking van basisklassen te wijzigen. Of u de basis-implementatie aanroept vóór of na uw overschrijvende code, is afhankelijk van de aard van uw implementatie.
Verwerking van gebeurtenisklasse voor invoer
De virtuele methoden van de klasse-handler zijn allemaal geregistreerd, zodat ze alleen worden aangeroepen in gevallen waarin gedeelde gebeurtenisgegevens nog niet zijn gemarkeerd als afgehandeld. Ook worden voor de invoerevenementen de tunneling- en bubblingversies doorgaans in volgorde opgewekt en delen zij dezelfde gebeurtenisgegevens. Dit houdt in dat voor een bepaald paar klassebehandelaars van invoergebeurtenissen, waar de ene een tunnelingversie is en de andere een bubblingversie, u de gebeurtenis mogelijk niet direct als afgehandeld wilt markeren. Als u de virtuele methode voor het afhandelen van de tunnelingklasse implementeert om te markeren dat de gebeurtenis wordt afgehandeld, voorkomt u dat de bubblingklasse-handler wordt aangeroepen (dit verhindert ook dat normaal geregistreerde instance-handlers voor zowel de tunneling- als de bubbling-gebeurtenis worden aangeroepen).
Zodra de klasseverwerking op een knooppunt is voltooid, worden de instantiebeluisteraars overwogen.
Exemplaarhandlers toevoegen die worden geactiveerd, zelfs als de gebeurtenissen als afgehandeld zijn gemarkeerd.
De AddHandler methode levert een bepaalde overbelasting waarmee u handlers kunt toevoegen die door het gebeurtenissysteem worden aangeroepen wanneer een gebeurtenis het verwerkingselement in de route bereikt, zelfs als een andere handler de gebeurtenisgegevens al heeft aangepast om die gebeurtenis te markeren als verwerkt. Dit wordt meestal niet gedaan. Over het algemeen kunnen handlers worden geschreven om alle gebieden van toepassingscode aan te passen die kunnen worden beïnvloed door een gebeurtenis, ongeacht waar deze in een elementstructuur is verwerkt, zelfs als meerdere eindresultaten gewenst zijn. Meestal is er ook slechts één element dat op die gebeurtenis moet reageren en dat de juiste toepassingslogica al was uitgevoerd. Maar de handledEventsToo
overbelasting is beschikbaar voor de uitzonderlijke gevallen waarin een ander element in een elementstructuur of controlecompositing al een gebeurtenis heeft gemarkeerd als verwerkt, maar andere elementen die hoger of lager zijn in de elementstructuur (afhankelijk van de route) nog steeds hun eigen handlers willen laten aanroepen.
Wanneer verwerkte gebeurtenissen als niet-verwerkt markeren
Over het algemeen mogen gerouteerde gebeurtenissen die als verwerkt zijn gemarkeerd, niet als onbewerkt worden aangegeven (Handled teruggezet naar false
), zelfs niet door handlers die op handledEventsToo
reageren. Sommige invoergebeurtenissen hebben echter gebeurtenisweergaven op hoog en lager niveau die elkaar kunnen overlappen wanneer de gebeurtenis op hoog niveau op één positie in de structuur wordt gezien en de gebeurtenis op laag niveau op een andere positie. Denk bijvoorbeeld aan het geval dat een kindelement luistert naar een toetsgebeurtenis op hoog niveau, zoals TextInput, terwijl een ouderelement luistert naar een gebeurtenis op laag niveau, zoals KeyDown. Als het bovenliggende element de gebeurtenis op laag niveau afhandelt, kan de gebeurtenis op een hoger niveau worden onderdrukt, zelfs in het onderliggende element dat intuïtief de eerste mogelijkheid moet hebben om de gebeurtenis af te handelen.
In deze situaties kan het nodig zijn om handlers toe te voegen aan zowel bovenliggende elementen als onderliggende elementen voor de gebeurtenis op laag niveau. De implementatie van de kindelementhandler kan de gebeurtenis op laag niveau markeren als verwerkt, maar de implementatie van de ouderelementhandler stelt deze opnieuw in als niet-verwerkt, zodat verdere elementen hoger in de structuur (evenals de gebeurtenis op hoog niveau) de mogelijkheid hebben om te reageren. Deze situatie moet vrij zeldzaam zijn.
Opzettelijk invoerevenementen onderdrukken voor besturingcompositie
Het belangrijkste scenario waarin klasseafhandeling van gerouteerde gebeurtenissen wordt gebruikt, is voor invoergebeurtenissen en samengestelde besturingselementen. Een samengesteld besturingselement bestaat per definitie uit meerdere praktische besturingselementen of basisklassen voor besturingselementen. Vaak wil de auteur van het besturingselement alle mogelijke invoergebeurtenissen die elk van de subonderdelen kan genereren, samenbrengen om het volledige besturingselement te rapporteren als de enkelvoudige gebeurtenisbron. In sommige gevallen kan de auteur van het besturingselement de gebeurtenissen van onderdelen volledig onderdrukken of een door een onderdeel gedefinieerde gebeurtenis vervangen die meer informatie bevat of een specifieker gedrag impliceert. Het canonieke voorbeeld dat direct zichtbaar is voor elke auteur van een onderdeel is hoe een WPF (Windows Presentation Foundation) Button elke muis gebeurtenis verwerkt die uiteindelijk wordt omgezet in de intuïtieve gebeurtenis die alle knoppen hebben: een Click gebeurtenis.
De Button basisklasse (ButtonBase) is afgeleid van Control die op zijn beurt is afgeleid van FrameworkElement en UIElement, en veel van de gebeurtenisinfrastructuur die nodig is voor het beheren van invoerverwerking, is beschikbaar op UIElement niveau. In het bijzonder, verwerkt UIElement algemene Mouse gebeurtenissen die het testen van treffers voor de muiscursor binnen zijn grenzen behandelen, en biedt het afzonderlijke gebeurtenissen voor de meest voorkomende knopacties, zoals MouseLeftButtonDown.
UIElement biedt ook een lege virtuele OnMouseLeftButtonDown aan als preregistreerde klassehandler voor MouseLeftButtonDown, en ButtonBase overschrijft deze. Op dezelfde manier gebruikt ButtonBase klasse-handlers voor MouseLeftButtonUp. In de overschrijvingen, die bij de gebeurtenisgegevens worden doorgegeven, markeren de implementaties het RoutedEventArgs-exemplaar als verwerkt door Handled in te stellen op true
, en diezelfde gebeurtenisgegevens worden voortgezet langs de rest van de route naar andere klassehandlers en ook naar instantiehandlers of gebeurtenissetters. Ook zal de OnMouseLeftButtonUp overschrijving vervolgens de Click gebeurtenis activeren. Het eindresultaat voor de meeste listeners is dat de gebeurtenissen MouseLeftButtonDown en MouseLeftButtonUp 'verdwijnen' en in plaats daarvan worden vervangen door Click, een gebeurtenis die meer betekenis heeft omdat het bekend is dat deze gebeurtenis afkomstig is van een echte knop en niet een samengesteld stuk van de knop of van een ander element volledig.
Omzeilen van gebeurtenisonderdrukking door middel van besturingselementen
Soms kan dit gedrag voor het onderdrukken van gebeurtenissen binnen afzonderlijke besturingselementen een aantal algemenere intenties van gebeurtenisafhandelingslogica voor uw toepassing verstoren. Als uw toepassing bijvoorbeeld om een of andere reden een handler voor MouseLeftButtonDown had die zich in het hoofdelement van de toepassing bevindt, zult u merken dat een muisklik op een knop geen MouseLeftButtonDown of MouseLeftButtonUp handlers op het wortelniveau aanroept. De gebeurtenis zelf is daadwerkelijk omhoog gekomen (het is zo dat gebeurtenisroutes niet werkelijk worden beëindigd, maar het systeem voor gerouteerde gebeurtenissen verandert het oproepgedrag van handlers nadat ze zijn gemarkeerd als afgehandeld). Wanneer de gerouteerde gebeurtenis de knop heeft bereikt, heeft de ButtonBase klasse-afhandeling de MouseLeftButtonDown als verwerkt gemarkeerd omdat het de Click gebeurtenis wilde vervangen door een zinvollere. Daarom wordt elke standaard-MouseLeftButtonDown handler verderop de route niet aangeroepen. Er zijn twee technieken die u kunt gebruiken om ervoor te zorgen dat uw handlers in deze situatie worden aangeroepen.
De eerste techniek is het opzettelijk toevoegen van de handler met behulp van de handledEventsToo
handtekening van AddHandler(RoutedEvent, Delegate, Boolean). Een beperking van deze benadering is dat deze techniek voor het koppelen van een eventhandler alleen mogelijk is vanuit code, niet vanuit markup. De eenvoudige syntaxis van het opgeven van de naam van de gebeurtenis-handler als een waarde van het gebeurteniskenmerk via Extensible Application Markup Language (XAML) schakelt dat gedrag niet in.
De tweede techniek werkt alleen voor invoergebeurtenissen, waarbij de tunneling- en bubblingversies van de gerouteerde gebeurtenis worden gekoppeld. Voor deze gerouteerde gebeurtenissen kunt u in plaats daarvan handlers toevoegen aan de equivalente gerouteerde gebeurtenis van preview/tunneling. Deze gerouteerde gebeurtenis zal tunnelen door de route die begint bij de wortel, waardoor de code voor het verwerken van de knopklasse deze niet zal onderscheppen, ervan uitgaande dat u de Preview-handler op een bovenliggend elementniveau in de elementenboom van de toepassing hebt gekoppeld. Als u deze benadering gebruikt, moet u voorzichtig zijn met het markeren van een Preview-evenement als afgehandeld. Als het voorbeeld aangeeft dat PreviewMouseLeftButtonDown bij het hoofdelement wordt verwerkt en u markeert de gebeurtenis als Handled in de handler-implementatie, onderdrukt u daarmee daadwerkelijk de Click-gebeurtenis. Dat is doorgaans niet wenselijk gedrag.
Zie ook
.NET Desktop feedback