Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
I když neexistuje žádné absolutní pravidlo, kdy označit směrovanou událost jako zpracovávanou, zvažte označení události jako zpracovávané, pokud váš kód reaguje na událost významným způsobem. Směrovaná událost označená jako zpracovaná bude pokračovat podél své trasy, ale vyvolají se pouze obslužné funkce nakonfigurované tak, aby reagovaly na zpracované události. V podstatě, označení směrované události jako zpracované omezuje její viditelnost pro posluchače podél trasy události.
Směrované obslužné rutiny událostí mohou být buď rutinami instance, nebo rutinami třídy. Obslužné rutiny instancí zpracovávají směrované události na objektech nebo elementech XAML. Obslužné rutiny třídy zpracovávají směrovanou událost na úrovni třídy a jsou vyvolány před jakoukoli obslužnou rutinou instance reagující na stejnou událost v jakékoli instanci třídy. Když jsou směrované události označeny jako zpracované, bývají často označovány takto v obslužných rutinách třídy. Tento článek se zabývá výhodami a potenciálními nástrahami označení směrovaných událostí jako zpracovaných, různými typy směrovaných událostí a jejich obslužných rutin a potlačením událostí ve složených ovládacích prvcích.
Požadavky
Tento článek předpokládá základní znalost trasovaných událostí a že jste si přečetli přehled trasovaných událostí. Pokud chcete postupovat podle příkladů v tomto článku, pomůže vám to, pokud znáte jazyk XAML (Extensible Application Markup Language) a víte, jak psát aplikace WINDOWS Presentation Foundation (WPF).
Kdy označit směrované události jako zpracované
Obvykle by pro každou směrovanou událost měla poskytovat významnou odpověď pouze jedna obslužná rutina. Nepoužívejte směrovaný systém událostí k zajištění významné odpovědi napříč několika obslužné rutinami. Definice toho, co představuje významnou odpověď, je subjektivní a závisí na vaší aplikaci. Obecné pokyny:
- Mezi významné odpovědi patří nastavení fokusu, úprava veřejného stavu, vlastnosti nastavení, které ovlivňují vizuální reprezentaci, vyvolávání nových událostí a úplné zpracování události.
- Mezi nevýznamné odpovědi patří změna privátního stavu bez vizuálního nebo programového dopadu, protokolování událostí a zkoumání dat událostí bez reakce na událost.
Některé ovládací prvky WPF potlačí události na úrovni komponent, které nepotřebují další zpracování tím, že je označí jako zpracovávané. Pokud chcete zpracovat událost, kterou ovládací prvek označil jako zpracovanou, viz téma Práce s potlačením událostí ovládacími prvky.
Pokud chcete událost označit jako zpracovanou, nastavte Handled hodnotu vlastnosti v datech události na truehodnotu . I když je možné vrátit se k této hodnotě false, nutnost to udělat, by měla být vzácná.
Náhledové a bublinové páry směrovaných událostí
Preview a bublání směrovaných událostí jsou specifické pro vstupní události. Několik vstupních událostí implementuje tunelování a bublající dvojici směrovaných událostí, například PreviewKeyDown a KeyDown. Předpona Preview označuje, že událost bublání se spustí po dokončení události náhledu. Každý pár událostí typu Preview a bublání sdílí stejnou instanci dat o událostech.
Obslužné rutiny směrovaných událostí se vyvolávají v pořadí, které odpovídá strategii směrování události:
- Událost preview se přesune z kořenového elementu aplikace dolů do elementu, který vyvolal směrovanou událost. Obslužné rutiny událostí náhledu připojené ke kořenovému prvku aplikace se vyvolávají jako první, následují rutiny připojené k postupně vnořeným prvkům.
- Po dokončení ukázkové události se spárovaná bublinová událost přesune z prvku, který vyvolal směrovanou událost, do kořenového prvku aplikace. Obslužné rutiny bublinových událostí připojené ke stejnému prvku, který vyvolal událost s trasováním, se spustí jako první, následované obslužnými rutinami připojenými k následným nadřazeným prvkům.
Spárované události náhledu a bublání jsou součástí interní implementace několika tříd WPF, které deklarují a vyvolávají vlastní směrované události. Bez této interní implementace na úrovni třídy jsou předběžné a bublinové směrované události zcela oddělené a nebudou sdílet data událostí – bez ohledu na pojmenování. Informace o tom, jak implementovat bublování nebo tunelování vstupních směrovaných událostí ve vlastní třídě, naleznete v tématu Vytvoření vlastní směrované události.
Vzhledem k tomu, že každá dvojice událostí náhledu a bublání sdílí stejnou instanci dat událostí, pokud je událost směrovaná ve verzi Preview označená jako zpracována, zpracuje se také její spárovaná událost bublání. Pokud je událost směrovaná nasměrovaná bublinou označená jako zpracována, nebude mít vliv na spárovanou událost náhledu, protože událost náhledu byla dokončena. Při označování náhledu a vybuchování párů vstupních událostí při zpracování buďte opatrní. Zpracovaná událost vstupu ve verzi preview nevyvolá žádné běžně zaregistrované obslužné rutiny událostí pro zbytek trasy tunelování a spárovaná událost bublování se nevyvolá. Obsloužená vstupní událost bublání nevyvolá žádné běžně registrované obslužné rutiny pro zbytek bublinové cesty.
Obslužné rutiny směrovaných událostí pro instance a třídy
Směrované obslužné rutiny událostí mohou být obslužné rutiny instance nebo obslužné rutiny třídy . Obslužné rutiny třídy pro danou třídu jsou vyvolány před jakoukoli obslužnou rutinou instance reagující na stejnou událost u jakékoli instance této třídy. Vzhledem k tomuto chování jsou směrované události označeny jako zpracované a často jsou takto označeny v obslužných rutinách třídy. Existují dva typy obsluh tříd:
- Obslužné rutiny událostí statické třídy, které jsou registrovány voláním RegisterClassHandler metody v rámci konstruktoru statické třídy.
- Přepsat obslužné rutiny událostí třídy, které jsou registrovány přepsáním metod virtuální události základní třídy. Metody virtuálních událostí základní třídy primárně existují pro vstupní události a mají názvy začínající na On<název události> a OnPreview<název události>.
Obslužné rutiny událostí instance
Obslužné rutiny instance můžete připojit k objektům nebo elementům XAML přímo voláním AddHandler metody. Směrované události WPF implementují obal událostí CLR (Common Language Runtime), který využívá metodu AddHandler k připojení obsluh událostí. Vzhledem k tomu, že syntaxe atributu XAML pro připojení obslužných procedur událostí vede k volání obálky událostí CLR, dokonce i připojení procedur v XAML se řeší jako AddHandler volání. Pro zpracovávané události:
- Obslužné rutiny, které jsou připojeny pomocí syntaxe atributu XAML nebo společný podpis
AddHandler, se nevyvolají. - Obslužné rutiny, které jsou připojeny pomocí přetížení AddHandler(RoutedEvent, Delegate, Boolean) s parametrem
handledEventsToonastaveným natrue, jsou vyvolány. Toto přetížení je k dispozici pro vzácné případy, kdy je nutné reagovat na zpracovávané události. Například některý prvek ve stromu elementu označil událost jako zpracována, ale další prvky podél trasy události musí reagovat na zpracovávanou událost.
Následující ukázka XAML přidá vlastní ovládací prvek s názvem componentWrapper, který zabalí pojmenovaný TextBoxcomponentTextBox, do pojmenovaného StackPanelouterStackPanel. Obslužná rutina události instance pro událost PreviewKeyDown je připojena k componentWrapper pomocí syntaxe atributu XAML. V důsledku toho bude obslužná rutina instance reagovat pouze na neošetřené události tunelování vyvolané PreviewKeyDown.
<StackPanel Name="outerStackPanel" VerticalAlignment="Center">
<custom:ComponentWrapper
x:Name="componentWrapper"
TextBox.PreviewKeyDown="HandlerInstanceEventInfo"
HorizontalAlignment="Center">
<TextBox Name="componentTextBox" Width="200" />
</custom:ComponentWrapper>
</StackPanel>
Konstruktor MainWindow připojí obslužnou rutinu instance k KeyDown události bublání k componentWrapper pomocí UIElement.AddHandler(RoutedEvent, Delegate, Boolean) přetížení, kde je parametr handledEventsToo nastaven na true. V důsledku toho obslužná rutina události instance odpoví na neošetřené i zpracovávané události.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
// Attach an instance handler on componentWrapper that will be invoked by handled KeyDown events.
componentWrapper.AddHandler(KeyDownEvent, new RoutedEventHandler(Handler.InstanceEventInfo),
handledEventsToo: true);
}
// The handler attached to componentWrapper in XAML.
public void HandlerInstanceEventInfo(object sender, KeyEventArgs e) =>
Handler.InstanceEventInfo(sender, e);
}
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
' Attach an instance handler on componentWrapper that will be invoked by handled KeyDown events.
componentWrapper.[AddHandler](KeyDownEvent, New RoutedEventHandler(AddressOf InstanceEventInfo),
handledEventsToo:=True)
End Sub
' The handler attached to componentWrapper in XAML.
Public Sub HandlerInstanceEventInfo(sender As Object, e As KeyEventArgs)
InstanceEventInfo(sender, e)
End Sub
End Class
Implementace kódu na pozadí u ComponentWrapper je zobrazena v další části.
Obslužné rutiny událostí statické třídy
Obslužné rutiny událostí statické třídy můžete připojit voláním RegisterClassHandler metody ve statickém konstruktoru třídy. Každá třída v hierarchii tříd může pro každou směrovanou událost zaregistrovat vlastní obslužnou rutinu statické třídy. V důsledku toho může být vyvoláno více obslužných rutin statických tříd pro stejnou událost na libovolném daném uzlu v trase události. Při vytvoření trasy pro událost se do ní přidají všechny obslužné rutiny statických tříd pro každý uzel. Pořadí vyvolání obslužných rutin statických tříd na uzlu začíná nejodvozenější obslužnou rutinou statické třídy, následovanou obslužnými rutinami statických tříd z každé postupné základní třídy.
Obslužné rutiny událostí statické třídy zaregistrované pomocí RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) přetížení s parametrem handledEventsToo nastaveným na true budou reagovat na ošetřené a neošetřené směrované události.
Obslužné rutiny statických tříd jsou obvykle registrovány tak, aby reagovaly pouze na neošetřené události. V takovém případě, pokud obslužná rutina odvozené třídy na uzlu označí událost jako zpracována, obslužné rutiny základní třídy pro tuto událost nebudou vyvolány. V tomto scénáři je obslužná rutina základní třídy účinně nahrazena odvozenou obslužnou rutinou třídy. Obslužné rutiny základní třídy často přispívají k návrhu ovládacích prvků v oblastech, jako je vzhled vizuálu, logika stavu, zpracování vstupu a zpracování příkazů, takže buďte opatrní při jejich nahrazení. Odvozené obslužné rutiny třídy, které neoznačí událost jako zpracovávané, končí doplněním obslužných rutin základní třídy místo jejich nahrazení.
Následující ukázka kódu ukazuje hierarchii tříd pro ComponentWrapper vlastní ovládací prvek, na který byl odkazován v předchozím kódu XAML. Třída ComponentWrapper je odvozena od ComponentWrapperBase třídy, která je zase odvozena od StackPanel třídy. Metoda RegisterClassHandler použitá ve statickém konstruktoru ComponentWrapper a ComponentWrapperBase třídách zaregistruje obslužnou rutinu události statické třídy pro každou z těchto tříd. Systém událostí WPF vyvolá obslužnou rutinu ComponentWrapper statické třídy před obslužnou rutinou ComponentWrapperBase statické třídy.
public class ComponentWrapper : ComponentWrapperBase
{
static ComponentWrapper()
{
// Class event handler implemented in the static constructor.
EventManager.RegisterClassHandler(typeof(ComponentWrapper), KeyDownEvent,
new RoutedEventHandler(Handler.ClassEventInfo_Static));
}
// Class event handler that overrides a base class virtual method.
protected override void OnKeyDown(KeyEventArgs e)
{
Handler.ClassEventInfo_Override(this, e);
// Call the base OnKeyDown implementation on ComponentWrapperBase.
base.OnKeyDown(e);
}
}
public class ComponentWrapperBase : StackPanel
{
// Class event handler implemented in the static constructor.
static ComponentWrapperBase()
{
EventManager.RegisterClassHandler(typeof(ComponentWrapperBase), KeyDownEvent,
new RoutedEventHandler(Handler.ClassEventInfoBase_Static));
}
// Class event handler that overrides a base class virtual method.
protected override void OnKeyDown(KeyEventArgs e)
{
Handler.ClassEventInfoBase_Override(this, e);
e.Handled = true;
Debug.WriteLine("The KeyDown routed event is marked as handled.");
// Call the base OnKeyDown implementation on StackPanel.
base.OnKeyDown(e);
}
}
Public Class ComponentWrapper
Inherits ComponentWrapperBase
Shared Sub New()
' Class event handler implemented in the static constructor.
EventManager.RegisterClassHandler(GetType(ComponentWrapper), KeyDownEvent,
New RoutedEventHandler(AddressOf ClassEventInfo_Static))
End Sub
' Class event handler that overrides a base class virtual method.
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
ClassEventInfo_Override(Me, e)
' Call the base OnKeyDown implementation on ComponentWrapperBase.
MyBase.OnKeyDown(e)
End Sub
End Class
Public Class ComponentWrapperBase
Inherits StackPanel
Shared Sub New()
' Class event handler implemented in the static constructor.
EventManager.RegisterClassHandler(GetType(ComponentWrapperBase), KeyDownEvent,
New RoutedEventHandler(AddressOf ClassEventInfoBase_Static))
End Sub
' Class event handler that overrides a base class virtual method.
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
ClassEventInfoBase_Override(Me, e)
e.Handled = True
Debug.WriteLine("The KeyDown event is marked as handled.")
' Call the base OnKeyDown implementation on StackPanel.
MyBase.OnKeyDown(e)
End Sub
End Class
Implementace obslužných rutin událostí přetížení třídy v kódovém zákulisí v této ukázce kódu je popsána v další části.
Přepsat obslužné programy událostí třídy
Některé základní třídy vizuálních prvků zpřístupňují prázdné On<název události> a OnPreview<název události> virtuální metody pro každou z jejich veřejných směrovaných vstupních událostí. Například UIElement implementuje virtuální obslužné rutiny událostí OnKeyDown a OnPreviewKeyDown, stejně jako mnoho dalších. Můžete přepsat virtuální obslužné rutiny událostí základní třídy pro vytvoření obslužných rutin událostí přetížení v odvozených třídách. Například můžete přidat obslužnou rutinu pro přepsání třídy pro událost DragEnter v jakékoli odvozené třídě UIElement tím, že přepíšete virtuální metodu OnDragEnter. Přepsání virtuálních metod základní třídy je jednodušší způsob implementace obslužných rutin třídy než registrace obslužných rutin tříd ve statickém konstruktoru. V rámci přepsání můžete vyvolat události, zahájit logiku specifickou pro třídu, změnit vlastnosti prvku u instancí, označit událost jako vyřízenou, nebo provést jinou logiku pro zpracování událostí.
Na rozdíl od obslužných rutin událostí statických tříd vyvolá systém událostí WPF přepis ovladačů událostí pouze pro nejodvozenější třídu v hierarchii tříd. Nejvýraznější třída v hierarchii tříd pak může použít základní klíčové slovo k volání základní implementace virtuální metody. Ve většině případů byste měli volat základní implementaci bez ohledu na to, jestli událost označíte jako zpracovanou. Volání základní implementace byste měli vynechat pouze v případě, že vaše třída má požadavek na nahrazení základní logiky implementace, pokud existuje. Zda zavoláte základní implementaci před nebo po úpravě kódu, závisí na povaze vaší implementace.
V předchozí ukázce kódu je virtuální metoda základní třídy OnKeyDown přepsána v obou třídách, tedy ve třídách ComponentWrapper a ComponentWrapperBase. Vzhledem k tomu, že systém událostí WPF vyvolá pouze obslužnou rutinu události třídy přepsání ComponentWrapper.OnKeyDown, tato obslužná rutina používá base.OnKeyDown(e) k volání ComponentWrapperBase.OnKeyDown obslužné rutiny události třídy přepsání, která zase používá base.OnKeyDown(e) k volání StackPanel.OnKeyDown virtuální metody. Pořadí událostí v předchozí ukázce kódu je:
- Obslužná rutina instance připojená k
componentWrapperje aktivovánaPreviewKeyDownsměrovanou událostí. - Statická obslužná rutina třídy připojená k
componentWrapperje spuštěna směrovanou událostíKeyDown. - Statická obslužná rutina třídy připojená k
componentWrapperBaseje spuštěna směrovanou událostíKeyDown. - Obslužná rutina přepsání třídy připojená k
componentWrapperje aktivována směrovanou událostíKeyDown. - Obslužná rutina přepsání třídy připojená k
componentWrapperBaseje aktivována směrovanou událostíKeyDown. - Směrovaná
KeyDownudálost je označena jako zpracována. - Obslužná rutina instance připojená k
componentWrapperje aktivovánaKeyDownsměrovanou událostí. Obslužná rutina byla zaregistrována s parametrem nastavenýmhandledEventsToonatrue.
Potlačení vstupní události ve složených ovládacích prvcích
Některé složené ovládací prvky potlačí vstupní události na úrovni komponenty, aby je nahradily přizpůsobenou událostí vysoké úrovně, která obsahuje více informací nebo implikuje konkrétnější chování. 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ů. Klasickým příkladem je Button ovládací prvek, který transformuje různé události myši na Click směrovanou událost. Základní Button třída je ButtonBase, která nepřímo odvozena od UIElement. Velká část infrastruktury událostí potřebná ke zpracování vstupu řízení je k dispozici na UIElement úrovni.
UIElement odhaluje několik Mouse událostí, jako jsou MouseLeftButtonDown a MouseRightButtonDown.
UIElement také implementuje prázdné virtuální metody OnMouseLeftButtonDown a OnMouseRightButtonDown jako předregistrované obslužné rutiny třídy.
ButtonBase přepíše tyto obslužné rutiny třídy a v rámci obslužné rutiny přepsání nastaví Handled vlastnost na true a vyvolá Click událost. Konečným výsledkem většiny naslouchacích procesů je, že MouseLeftButtonDown události a MouseRightButtonDown události jsou skryté a událost vysoké úrovně Click je viditelná.
Práce s potlačením vstupních událostí
Někdy může potlačení událostí v rámci jednotlivých ovládacích prvků kolidovat s logikou zpracování událostí ve vaší aplikaci. Pokud například vaše aplikace použila syntaxi atributu XAML k připojení obslužné rutiny události MouseLeftButtonDown v kořenovém elementu XAML, nebude tato obslužná rutina vyvolána, protože Button ovládací prvek označí MouseLeftButtonDown událost jako zpracována. Pokud chcete, aby se elementy směrem ke kořenovému adresáři vaší aplikace vyvolaly pro zpracovávanou směrovanou událost, můžete:
Připojte obslužné rutiny voláním UIElement.AddHandler(RoutedEvent, Delegate, Boolean) metody s parametrem nastaveným
handledEventsToonatrue. Tento přístup vyžaduje připojení obslužné rutiny události v kódu na pozadí, po získání referenčního objektu pro prvek, ke kterému se připojí.Pokud je událost označená jako zpracovávaná událost vkladu, připojte obslužné rutiny pro párovou událost náhledu, pokud je k dispozici. Pokud například ovládací prvek potlačí
MouseLeftButtonDownudálost, můžete místo toho připojit obslužnou rutinu PreviewMouseLeftButtonDown události. Tento přístup funguje jen u párů vstupních událostí typu preview a bubbling, které sdílejí údaje o událostech. Dávejte pozor, abyste neoznačiliPreviewMouseLeftButtonDownjako vyřízené, protože by to zcela potlačilo událost Click.
Příklad, jak obejít potlačení vstupní události, naleznete v tématu Práce s potlačením událostí ovládacími prvky.
Viz také
.NET Desktop feedback