Označení směrovaných událostí jako zpracovaných a zpracování tříd

Obslužné rutiny pro směrovanou událost můžou označit událost zpracovávanou v datech události. Zpracování události efektivně zkracuje trasu. Zpracování tříd je programovací koncept podporovaný směrovanými událostmi. Obslužná rutina třídy má možnost zpracovat konkrétní směrovanou událost na úrovni třídy pomocí obslužné rutiny, která je vyvolána před jakoukoli obslužnou rutinou instance v jakékoli instanci třídy.

Předpoklady

Toto téma popisuje koncepty představené v přehledu směrovaných událostí.

Kdy označit události jako zpracovávané

Když nastavíte hodnotu Handled vlastnosti na true data události pro směrovanou událost, označuje se to jako "označení obslužné události". Neexistuje žádné absolutní pravidlo, kdy byste měli označit směrované události jako zpracovávané, buď jako autor aplikace, nebo jako autor ovládacího prvku, který reaguje na existující směrované události nebo implementuje nové směrované události. Ve většině případů by se koncept "zpracovával" tak, jak se přenáší v datech událostí směrované události, použit jako omezený protokol pro odpovědi vaší vlastní aplikace na různé směrované události zveřejněné v rozhraních API WPF a také pro všechny vlastní směrované události. Dalším způsobem, jak zvážit problém "zpracovávaný", je, že byste obecně měli označit směrovanou událost zpracovávanou, pokud váš kód reagoval na směrovanou událost významným a relativně úplným způsobem. Obvykle by nemělo existovat více než jedna významná odpověď, která vyžaduje samostatné implementace obslužné rutiny pro všechny výskyty jedné směrované události. Pokud potřebujete více odpovědí, je potřeba implementovat potřebný kód prostřednictvím logiky aplikace, která je zřetězený v rámci jedné obslužné rutiny, a ne pomocí směrovaného systému událostí pro předávání. Koncept toho, co je "významné", je také subjektivní a závisí na vaší aplikaci nebo kódu. Mezi obecné pokyny patří některé příklady "významné odpovědi": nastavení fokusu, úprava veřejného stavu, nastavení vlastností, které ovlivňují vizuální reprezentaci, a vyvolávání dalších nových událostí. Mezi příklady nepodepsaných odpovědí patří: úprava soukromého stavu (bez vizuálního dopadu nebo programového vyjádření), protokolování událostí nebo zobrazení argumentů události a volba, že na ni neodpovědíte.

Chování směrovaného systému událostí posiluje tento model "významné odezvy" pro použití zpracovávaného stavu směrované události, protože obslužné rutiny přidané v xaml nebo společný podpis AddHandler nejsou vyvolány v reakci na směrovanou událost, kde jsou data události již označena jako zpracována. Pokud chcete zpracovávat směrované události označené dřívějšími účastníky v trase události, musíte projít dodatečným úsilím o přidání obslužné rutiny s handledEventsToo verzí parametru (AddHandler(RoutedEvent, Delegate, Boolean)).

V některých případech se ovládací prvky označují jako zpracovávané určité směrované události. Obslužná směrovaná událost představuje rozhodnutí autory ovládacích prvků WPF, že akce ovládacího prvku v reakci na směrovanou událost jsou významné nebo úplné v rámci implementace řízení a událost nepotřebuje žádné další zpracování. Obvykle se to provádí přidáním obslužné rutiny třídy pro událost nebo přepsáním jedné z virtuálních rutin třídy, které existují v základní třídě. V případě potřeby můžete toto zpracování událostí obejít; Viz Práce s potlačením událostí ovládacími prvky dále v tomto tématu.

Události preview (tunelování) vs. události bublání a zpracování událostí

Směrované události ve verzi Preview jsou události, které sledují trasu tunelování přes strom elementů. Verze Preview vyjádřená v zásadách vytváření názvů indikuje obecný princip vstupních událostí, které směrované události ve verzi Preview (tunelování) jsou vyvolány před ekvivalentní událostí se směrováním bublin. Vstupní směrované události, které mají dvojici tunelování a bublinového propojení, mají také odlišnou logiku zpracování. Pokud je událost směrování tunelu nebo náhledu označena jako zpracována naslouchacím procesem událostí, bude událost s bublinovou trasou označena jako zpracována i před tím, než ji obdrží všechny naslouchací procesy směrované události. Tunelové a bublující směrované události jsou technicky samostatné události, ale záměrně sdílejí stejnou instanci dat událostí, aby toto chování umožnilo.

Propojení mezi tunelovými a bublajícími směrovanými událostmi je dosaženo interní implementací toho, jak každá daná třída WPF vyvolává své vlastní deklarované směrované události, a to platí pro spárované vstupní směrované události. Pokud však tato implementace na úrovni třídy neexistuje, neexistuje žádné spojení mezi událostí směrování tunelu a událostí s bublinovou trasou, která sdílí schéma pojmenování: bez takové implementace by byly dvě zcela samostatné směrované události a nebyly vyvolány v posloupnosti nebo sdílet data událostí.

Další informace o tom, jak implementovat dvojice trasovaných událostí tunelu nebo bublin ve vlastní třídě, naleznete v tématu Vytvoření vlastní směrované události.

Obslužné rutiny tříd a obslužné rutiny instancí

Směrované události považují za události dva různé typy naslouchacích procesů: naslouchací procesy tříd a naslouchací procesy instancí. Naslouchací procesy třídy existují, protože typy volají konkrétní EventManager rozhraní APIRegisterClassHandler , v jejich statickém konstruktoru nebo přepsaly virtuální metodu obslužné rutiny třídy ze základní třídy elementu. Instance naslouchací procesy jsou konkrétní instance třídy /prvky, kde jeden nebo více obslužných rutin byly připojeny pro tuto směrovanou událost voláním AddHandler. Existující směrované události WPF volat AddHandler jako součást obálky událostí CLR (Common Language Runtime) přidání{} a odebrání{} implementací události, což je také způsob, jak je povolený jednoduchý mechanismus XAML pro připojení obslužných rutin událostí prostřednictvím syntaxe atributů. Proto i jednoduché použití XAML nakonec odpovídá AddHandler volání.

Prvky ve stromu vizuálu jsou kontrolovány pro registrované implementace obslužné rutiny. Obslužné rutiny se dají v celé trase vyvolat v pořadí, v jakém je podstata typu strategie směrování pro danou směrovanou událost. Například bublinové směrované události nejprve vyvolá ty obslužné rutiny, které jsou připojené ke stejnému prvku, který vyvolal směrovanou událost. Pak směrovaná událost "bubliny" do dalšího nadřazeného elementu atd., dokud se nedosáhne kořenového elementu aplikace.

Z pohledu kořenového prvku v bublinové trase, pokud zpracování třídy nebo jakýkoli prvek blíže ke zdroji obslužných rutin volání směrované události, které označí argumenty události jako zpracovávané, obslužné rutiny na kořenových prvcích se nevyvolají a trasa události se před dosažením tohoto kořenového prvku efektivně zkracuje. Trasa však není úplně zastavená, protože obslužné rutiny je možné přidat pomocí speciální podmínky, kterou by měly být stále vyvolány, i když obslužná rutina třídy nebo obslužná rutina instance označila směrovanou událost jako zpracována. To je vysvětleno v přidávání obslužných rutin instancí, které jsou vyvolány i v případě, že jsou události označeny jako zpracovány, dále v tomto tématu.

Na hlubší úrovni než trasa událostí existuje také několik obslužných rutin třídy fungujících na jakékoli dané instanci třídy. Důvodem je to, že model zpracování tříd pro směrované události umožňuje všem možným třídám v hierarchii tříd zaregistrovat vlastní obslužnou rutinu třídy pro každou směrovanou událost. Každá obslužná rutina třídy se přidá do interního úložiště a při vytvoření trasy události pro aplikaci se do trasy události přidají obslužné rutiny třídy. Obslužné rutiny třídy jsou přidány na trasu tak, aby byla obslužná rutina odvozené třídy vyvolána jako první a obslužné rutiny tříd z každé po sobě jdoucí základní třídy jsou vyvolány dále. Obecně platí, že obslužné rutiny tříd nejsou registrovány tak, aby také reagovaly na směrované události, které byly již označeny jako zpracovány. Tento mechanismus zpracování tříd proto umožňuje jednu ze dvou možností:

  • Odvozené třídy mohou doplnit zpracování třídy, které je zděděno ze základní třídy přidáním obslužné rutiny, která neoznačuje směrovanou událost obslužnou rutinu, protože obslužná rutina základní třídy bude vyvolána někdy po odvozené obslužné rutině třídy.

  • Odvozené třídy mohou nahradit zpracování třídy ze základní třídy přidáním obslužné rutiny třídy, která označuje směrovaná událost zpracovávaná. U tohoto přístupu byste měli být opatrní, protože potenciálně změní zamýšlený návrh základního ovládacího prvku v oblastech, jako je vzhled vizuálu, logika stavu, zpracování vstupu a zpracování příkazů.

Zpracování tříd směrovaných událostí podle základních tříd ovládacího prvku

Na každém daném uzlu elementu v trase události mají naslouchací procesy třídy možnost reagovat na směrovanou událost dříve, než může jakýkoli naslouchací proces instance prvku. Z tohoto důvodu se obslužné rutiny třídy někdy používají k potlačení směrovaných událostí, které konkrétní implementace třídy ovládacího prvku nechce dále rozšířit nebo poskytnout zvláštní zpracování této směrované události, která je funkcí třídy. Třída může například vyvolat vlastní událost specifickou pro třídu, která obsahuje konkrétnější informace o tom, co určitá podmínka vstupu uživatele znamená v kontextu dané třídy. Implementace třídy pak může označit obecnější směrovanou událost jako zpracována. Obslužné rutiny třídy se obvykle přidávají tak, že se nevyvolávají pro směrované události, u kterých byla již označena sdílená data událostí, ale v případě neobvyklých případů existuje také RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) podpis, který registruje obslužné rutiny třídy k vyvolání i v případě, že jsou směrované události označeny jako zpracovány.

Virtuals obslužné rutiny třídy

Některé prvky, zejména základní prvky, jako UIElementjsou například , zpřístupňují prázdné metody On*Event a OnPreview*Event, které odpovídají jejich seznamu veřejných směrovaných událostí. Tyto virtuální metody lze přepsat, aby implementovaly obslužnou rutinu třídy pro danou směrovanou událost. Třídy základních elementů registrují tyto virtuální metody jako jejich obslužnou rutinu třídy pro každou takovou směrovanou událost pomocí RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) , jak je popsáno výše. Virtuální metody On*Event usnadňují implementaci zpracování tříd pro příslušné směrované události bez nutnosti speciální inicializace statických konstruktorů pro každý typ. Například můžete přidat zpracování tříd pro DragEnter událost v jakékoli UIElement odvozené třídě přepsáním OnDragEnter virtuální metody. V rámci přepsání můžete zpracovat směrovanou událost, vyvolat další události, zahájit logiku specifickou pro třídu, která může změnit vlastnosti elementu v instancích nebo jakoukoli kombinaci těchto akcí. Obecně byste měli volat základní implementaci v takových přepsání, i když označíte obslužnou událost. Volání základní implementace se důrazně doporučuje, protože virtuální metoda je na základní třídě. Standardní chráněný virtuální vzor volání základních implementací z každé virtuální v podstatě nahrazuje a paralelně podobný mechanismus, který je nativní pro zpracování směrované třídy třídy, přičemž obslužné rutiny tříd pro všechny třídy v hierarchii tříd jsou volány na libovolné dané instanci, počínaje obslužnou rutinou třídy nejvíce odvozené třídy a pokračuje k obslužné rutině základní třídy. Základní volání implementace byste měli vynechat pouze v případě, že vaše třída má záměrný požadavek na změnu logiky zpracování základní třídy. Bez ohledu na to, jestli zavoláte základní implementaci před přepsáním nebo po přepsání kódu, bude záviset na povaze implementace.

Zpracování vstupní třídy událostí

Virtuální metody obslužné rutiny třídy jsou registrovány tak, že jsou vyvolány pouze v případech, kdy žádná sdílená data událostí ještě nejsou označena jako zpracována. U vstupních událostí jsou také jedinečně vyvolány verze tunelování a bublání v posloupnosti a sdílení dat událostí. To znamená, že pro danou dvojici obslužných rutin třídy vstupních událostí, kde jedna je verze tunelového propojení a druhá je verze bublání, možná nebudete chtít událost okamžitě označit. Pokud implementujete virtuální metodu zpracování třídy tunelování, která označí obslužnou rutinu události, která zabrání vyvolání obslužné rutiny třídy bublání (a také brání vyvolání všech běžně registrovaných obslužných rutin instancí pro vyvolání tunelové nebo bublající události).

Po dokončení zpracování třídy na uzlu se naslouchací procesy instance považují za naslouchací procesy.

Přidání obslužných rutin instancí, které jsou vyvolány i v případě, že jsou události označeny jako zpracovávané

Metoda AddHandler poskytuje konkrétní přetížení, které umožňuje přidat obslužné rutiny, které budou vyvolány systémem událostí, kdykoli událost dosáhne prvku zpracování v trase, i když některé jiné obslužné rutiny již upravily data události tak, aby označila tuto událost jako zpracována. Obvykle to není hotové. Obecně lze obslužné rutiny zapsat tak, aby upravovaly všechny oblasti kódu aplikace, které můžou být ovlivněny událostí, bez ohledu na to, kde byla zpracována ve stromu prvků, i když je žádoucí více koncových výsledků. Obvykle existuje pouze jeden prvek, který musí reagovat na danou událost a příslušná logika aplikace se už stala. handledEventsToo Přetížení je však k dispozici pro výjimečné případy, kdy některé další prvky ve stromu elementu nebo ovládací prvek kompoziting již označil událost jako zpracovávané, ale jiné prvky buď vyšší nebo nižší ve stromu elementů (v závislosti na trase), stále chtějí mít své vlastní obslužné rutiny vyvolány.

Kdy označit zpracovávané události jako neošetřené

Obecně platí, že směrované události, které jsou označené popisovačem, by neměly být označeny neošetřené (Handled nastaveny zpět false) ani obslužnými rutinami, které pracují s handledEventsToo. Některé vstupní události však mají vysoké a nižší úrovně reprezentace událostí, které se můžou překrývat, když se událost vysoké úrovně zobrazí na jedné pozici ve stromu a událost nízké úrovně na jiné pozici. Představte si například případ, kdy podřízený prvek naslouchá události klíče vysoké úrovně, například TextInput když nadřazený prvek naslouchá události nízké úrovně, například KeyDown. Pokud nadřazený prvek zpracovává událost nízké úrovně, může být událost vyšší úrovně potlačena i v podřízeného elementu, který by intuitivně měl mít první příležitost zpracovat událost.

V těchto situacích může být nutné přidat obslužné rutiny do nadřazených elementů i podřízených elementů pro událost nízké úrovně. Implementace obslužné rutiny podřízeného elementu může označit událost nízké úrovně jako zpracována, ale nadřazená implementace obslužné rutiny elementu by ji znovu neošetřela tak, aby další prvky stromu (stejně jako událost vysoké úrovně) mohly mít možnost reagovat. Tato situace by měla být poměrně vzácná.

Záměrně potlačování vstupních událostí pro kompozitování ovládacích prvků

Hlavní scénář, ve kterém se používá zpracování směrovaných událostí, je určené pro vstupní události a složené ovládací prvky. Složený ovládací prvek se skládá z několika praktických ovládacích prvků nebo základních tříd ovládacích prvků. Autor ovládacího prvku často chce amalgamovat všechny možné vstupní události, které můžou každou dílčí součást vyvolat, aby se celý ovládací prvek hlásil jako jediný zdroj událostí. V některých případech může autor ovládacího prvku chtít potlačit události z komponent zcela nebo nahradit událost definovanou komponentou, která obsahuje více informací nebo naznačuje konkrétnější chování. Kanonický příklad, který je okamžitě viditelný všem autorům komponent, je způsob, jakým Windows Presentation Foundation (WPF) Button zpracovává jakoukoli událost myši, která se nakonec přeloží na intuitivní událost, kterou mají všechna tlačítka: Click událost.

Základní Button třída (ButtonBase) je odvozena, z Control níž je odvozena FrameworkElementUIElementvelká část infrastruktury událostí potřebná pro řízení zpracování vstupu je k dispozici na UIElement úrovni. Konkrétně zpracovává obecné události, UIElement které zpracovávají testování kurzoru myši v jeho mezích, a poskytuje jedinečné události pro nejběžnější akce tlačítek, například MouseLeftButtonDown.Mouse UIElementtaké poskytuje prázdný virtuální OnMouseLeftButtonDown jako předregistrovanou obslužnou rutinu třídy a MouseLeftButtonDownButtonBase přepíše ji. ButtonBase Podobně používá obslužné rutiny tříd pro MouseLeftButtonUp. V přepsání, které jsou předány data události, implementace označí tuto RoutedEventArgs instanci jako zpracována nastavením Handled na truea že stejná data událostí jsou to, co pokračuje po zbytek trasy do jiných obslužných rutin třídy a také obslužné rutiny instance nebo setter událostí. Také přepsání OnMouseLeftButtonUp vyvolá Click událost. Konečným výsledkem většiny naslouchacích procesů bude, že MouseLeftButtonDown události MouseLeftButtonUp "zmizí" a jsou nahrazeny událostí Click, která má větší význam, protože je známo, že tato událost pochází z pravého tlačítka, a ne z nějaké složené části tlačítka nebo z jiného prvku zcela.

Práce s potlačením událostí ovládacími prvky

Někdy může toto chování potlačení událostí v rámci jednotlivých ovládacích prvků ovlivnit některé obecnější záměry logiky zpracování událostí pro vaši aplikaci. Pokud například vaše aplikace z nějakého důvodu měla obslužnou rutinu pro MouseLeftButtonDown umístění v kořenovém prvku aplikace, všimli byste si, že jakékoli kliknutí myší na tlačítko by nevolal MouseLeftButtonDown ani MouseLeftButtonUp obslužné rutiny na kořenové úrovni. Samotná událost skutečně skončila (opět nejsou trasy událostí skutečně ukončeny, ale směrovaný systém událostí změní chování vyvolání obslužné rutiny po označení popisovače). Když směrovaná událost dosáhla tlačítka, ButtonBase zpracování třídy označilo MouseLeftButtonDown popisovač, protože chtělo nahradit Click událost více významem. Proto by nebyla vyvolána žádná standardní MouseLeftButtonDown obslužná rutina dále v trase. Existují dvě techniky, které můžete použít k zajištění, aby obslužné rutiny byly vyvolány v této situaci.

První technikou je úmyslně přidat obslužnou rutinu pomocí handledEventsToo podpisu .AddHandler(RoutedEvent, Delegate, Boolean) Omezení tohoto přístupu spočívá v tom, že tato technika pro připojení obslužné rutiny události je možná pouze z kódu, nikoli z kódu. Jednoduchá syntaxe zadání názvu obslužné rutiny události jako hodnoty atributu události prostřednictvím jazyka XAML (Extensible Application Markup Language) toto chování neumožňuje.

Druhá technika funguje pouze u vstupních událostí, kdy se spárují tunelové a bublinové verze směrované události. U těchto směrovaných událostí můžete místo toho přidat obslužné rutiny do ekvivalentní směrované události ve verzi Preview nebo tunelování. Tato směrovaná událost bude tunelovat trasu počínaje kořenem, takže kód zpracování třídy tlačítka by ho nezachytil. Předpokládá se, že jste obslužnou rutinu Preview připojili na určité úrovni nadřazeného prvku ve stromu elementů aplikace. Pokud použijete tento přístup, buďte opatrní při označování všech zpracovaných událostí ve verzi Preview. V příkladu, který se PreviewMouseLeftButtonDown zpracovává v kořenovém elementu, pokud jste událost označili jako Handled v implementaci obslužné rutiny, byste skutečně potlačit Click událost. To obvykle není žádoucí chování.

Viz také