Označení směrovaných událostí jako zpracovaných a zpracování tříd (WPF .NET)
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 popisovaná bude pokračovat podél své trasy, ale vyvolá se pouze obslužné rutiny nakonfigurované tak, aby reagovaly na zpracovávané události. Označení směrované události v podstatě omezuje jeho viditelnost na naslouchací procesy podél trasy události.
Směrované obslužné rutiny událostí mohou být obslužné rutiny instance nebo obslužné rutiny třídy. Obslužné rutiny instancí zpracovávají směrované události u objektů nebo elementů 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. Při označení směrovaných událostí jako popisovaných událostí se často označují jako takové v rámci obslužných rutin tříd. Tento článek popisuje výhody a potenciální nástrahy označení směrovaných událostí jako zpracovaných, různé typy směrovaných událostí a obslužných rutin trasovaných událostí a potlačení událostí ve složených ovládacích prvcích.
Požadavky
V článku se předpokládá základní znalost směrovaných událostí a že jste si přečetli přehled směrovaný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 zpracovávané
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, která byla označena jako zpracována ovládacím prvku, přečtěte si 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 true
hodnotu . 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áhled a bublinové dvojice směrovaných událostí
Dvojice směrovaných událostí ve verzi Preview a bublání 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í verze Preview a bublání sdílí stejnou instanci dat událostí.
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é k kořenovému prvku aplikace se vyvolávají jako první a obslužné rutiny připojené k následným vnořeným prvkům.
- Po dokončení události ve verzi Preview se spárovaná bublinová událost přesune z elementu, který vyvolal směrovanou událost do kořenového prvku aplikace. Bublinové obslužné rutiny událostí připojené ke stejnému prvku, který vyvolal směrovanou událost, se vyvolá 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 interní implementace na úrovni třídy jsou směrované události verze Preview a bublinové směrované události zcela oddělené a nebudou sdílet data událostí – bez ohledu na pojmenování událostí. 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í. Obslužná událost vstupu ve verzi Preview nevolá žádné běžně zaregistrované obslužné rutiny událostí pro zbytek trasy tunelování a spárovaná událost bublování se nevyvolá. Obslužná vstupní událost bublování nevyvolá žádné obvykle registrované obslužné rutiny událostí pro zbytek trasy bublání.
Obslužné rutiny událostí směrovaných instancí a tříd
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 popisované, jsou často označeny jako takové v rámci obslužných rutin třídy. Existují dva typy obslužných rutin 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 název> události On<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í obálku událostí CLR (Common Language Runtime), která používá metodu AddHandler
pro připojení obslužných rutin událostí. Vzhledem k tomu, že syntaxe atributu XAML pro připojení obslužných rutin událostí vede k volání obálky událostí CLR, dokonce i připojení obslužných rutin v XAML překládá na 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 připojené pomocí AddHandler(RoutedEvent, Delegate, Boolean) přetížení se
handledEventsToo
sadou parametrů jsoutrue
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ý TextBox componentTextBox
, do pojmenovaného StackPanel outerStackPanel
. Obslužná rutina události instance pro PreviewKeyDown událost je připojena k syntaxi atributu componentWrapper
XAML. V důsledku toho obslužná rutina instance bude reagovat pouze na neošetřené PreviewKeyDown
události tunelování vyvolané .componentTextBox
<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 pro KeyDown
událost bublání k componentWrapper
použití UIElement.AddHandler(RoutedEvent, Delegate, Boolean) přetížení s parametrem nastaveným handledEventsToo
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 se ComponentWrapper
zobrazí 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 události události se do trasy události 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á nejvýraznější obslužnou rutinou statické třídy následované obslužnými rutinami statických tříd z každé po sobě jdoucí základní třídy.
Obslužné rutiny událostí statické třídy zaregistrované pomocí RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) přetížení se handledEventsToo
sadou true
parametrů budou reagovat na neošetřené i zpracovávané 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řepsání třídy v této ukázce kódu je popsána v další části.
Přepsání obslužných rutin 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 obslužné rutiny OnKeyDown virtuálních OnPreviewKeyDown událostí a mnoho dalších. Můžete přepsat virtuální obslužné rutiny virtuálních událostí základní třídy, aby implementovaly obslužné rutiny událostí přepsání třídy pro odvozené třídy. Můžete například přidat obslužnou rutinu přepsání třídy pro DragEnter událost v jakékoli UIElement
odvozené třídě přepsáním OnDragEnter virtuální metody. 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 a změnit vlastnosti elementu u instancí, označit událost jako zpracovávanou nebo provést jinou logiku zpracování událostí.
Na rozdíl od obslužných rutin událostí statické třídy vyvolá systém událostí WPF pouze přepsání obslužných rutin událostí třídy pro nejvíce odvozenou třídu v hierarchii třídy. 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. Bez ohledu na to, jestli voláte základní implementaci před nebo po přepsání kódu, závisí na povaze implementace.
V předchozí ukázce kódu se virtuální metoda základní třídy OnKeyDown
přepíše v obou třídách ComponentWrapper
ComponentWrapperBase
. Vzhledem k tomu, že systém událostí WPF vyvolá pouze obslužnou rutinu ComponentWrapper.OnKeyDown
události přepsání třídy, tato obslužná rutina používá base.OnKeyDown(e)
k volání ComponentWrapperBase.OnKeyDown
obslužné rutiny události přepsání třídy, 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á
PreviewKeyDown
jecomponentWrapper
aktivována směrovanou událostí. - Obslužná rutina statické třídy připojená
KeyDown
jecomponentWrapper
aktivována směrovanou událostí. - Obslužná rutina statické třídy připojená
KeyDown
jecomponentWrapperBase
aktivována směrovanou událostí. - Obslužná rutina přepsání třídy připojená
KeyDown
jecomponentWrapper
aktivována směrovanou událostí. - Obslužná rutina přepsání třídy připojená
KeyDown
jecomponentWrapperBase
aktivována směrovanou událostí. - Směrovaná
KeyDown
událost je označena jako zpracována. - Obslužná rutina instance připojená
KeyDown
jecomponentWrapper
aktivována směrovanou událostí. Obslužná rutina byla zaregistrována s parametrem nastavenýmhandledEventsToo
natrue
.
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
zveřejňuje několik Mouse událostí, jako MouseLeftButtonDown MouseRightButtonDownje a . 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
handledEventsToo
natrue
. Tento přístup vyžaduje připojení obslužné rutiny události za kódem po získání odkazu na objekt pro prvek, ke kterému se připojí.Pokud je událost označená jako popisovaná jako vstupní událost, připojte obslužné rutiny pro spárovanou událost náhledu( pokud je k dispozici). Pokud například ovládací prvek potlačí
MouseLeftButtonDown
událost, můžete místo toho připojit obslužnou rutinu PreviewMouseLeftButtonDown události. Tento přístup funguje jenom u párů vstupních událostí ve verzi Preview a bublání, které sdílejí data událostí. Dávejte pozor, abyste neoznačiliPreviewMouseLeftButtonDown
popisovač, protože by to událost zcela potlačí 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