Udostępnij za pośrednictwem


Oznaczanie zdarzenia trasowanego jako obsłużonego oraz obsługa klasy

Programy obsługi dla zdarzenia kierowanego mogą oznaczać zdarzenie obsługiwane w danych zdarzenia. Obsługa zdarzenia skutecznie skróci trasę. Obsługa klas to koncepcja programowania obsługiwana przez zdarzenia kierowane. Procedura obsługi klas ma możliwość obsługi określonego zdarzenia kierowanego na poziomie klasy za pomocą procedury obsługi, która jest wywoływana przed każdym programem obsługi wystąpień w dowolnym wystąpieniu klasy.

Wymagania wstępne

W tym temacie omówiono pojęcia wprowadzone w przeglądzie zdarzeń trasowanych.

Kiedy oznaczyć zdarzenia jako obsługiwane

Po ustawieniu wartości Handled właściwości na true w danych zdarzenia dla zdarzenia kierowanego jest to nazywane "oznaczanie obsłużonego zdarzenia". Nie ma reguły bezwzględnej, jeśli należy oznaczyć zdarzenia kierowane jako obsługiwane, jako autor aplikacji lub jako autor kontrolki, który reaguje na istniejące kierowane zdarzenia lub implementuje nowe zdarzenia kierowane. W większości przypadków pojęcie "obsługiwane" w danych zdarzenia kierowanego powinno być używane jako ograniczony protokół dla odpowiedzi własnej aplikacji na różne zdarzenia kierowane uwidocznione w interfejsach API WPF, a także dla wszelkich niestandardowych zdarzeń trasowanych. Innym sposobem rozważenia problemu "obsłużonego" jest to, że zazwyczaj należy oznaczyć zdarzenie kierowane obsługiwane, jeśli kod odpowiedział na zdarzenie kierowane w znaczący i stosunkowo kompletny sposób. Zazwyczaj nie powinno istnieć więcej niż jedna znacząca odpowiedź, która wymaga oddzielnych implementacji procedury obsługi dla żadnego wystąpienia pojedynczego zdarzenia kierowanego. Jeśli potrzebna jest większa odpowiedź, należy zaimplementować niezbędny kod za pomocą logiki aplikacji, która jest łańcuchowa w ramach pojedynczej procedury obsługi, a nie przy użyciu systemu zdarzeń kierowanych do przekazywania dalej. Pojęcie tego, co jest "znaczące", jest również subiektywne i zależy od aplikacji lub kodu. Ogólnie rzecz biorąc, niektóre przykłady "znaczącej odpowiedzi" obejmują: ustawianie fokusu, modyfikowanie stanu publicznego, ustawianie właściwości wpływających na reprezentację wizualizacji i wywoływanie innych nowych zdarzeń. Przykłady niepodpisanych odpowiedzi to: modyfikowanie stanu prywatnego (bez wpływu wizualnego lub reprezentacja programowa), rejestrowanie zdarzeń lub analizowanie argumentów zdarzenia i wybieranie, aby nie odpowiadać na nie.

Zachowanie systemu zdarzeń kierowanych wzmacnia ten "znaczący model odpowiedzi" na potrzeby używania obsługiwanego stanu kierowanego zdarzenia, ponieważ programy obsługi dodane w języku XAML lub wspólny podpis AddHandler nie są wywoływane w odpowiedzi na zdarzenie kierowane, w którym dane zdarzenia są już oznaczone jako obsługiwane. Należy wykonać dodatkowe czynności związane z dodawaniem programu obsługi z wersją handledEventsToo parametru (AddHandler(RoutedEvent, Delegate, Boolean)) w celu obsługi zdarzeń kierowanych oznaczonych przez wcześniejszych uczestników trasy.

W niektórych okolicznościach kontrolki same oznaczają określone zdarzenia kierowane zgodnie z obsługą. Obsługiwane zdarzenie kierowane reprezentuje decyzję autorów kontrolek WPF, że akcje kontrolki w odpowiedzi na kierowane zdarzenie są znaczące lub zakończone w ramach implementacji kontroli, a zdarzenie nie wymaga dalszej obsługi. Zazwyczaj odbywa się to przez dodanie programu obsługi klas dla zdarzenia lub zastąpienie jednej z maszyn wirtualnych programu obsługi klas, które istnieją w klasie bazowej. Nadal można obejść tę obsługę zdarzeń w razie potrzeby; w dalszej części tego tematu zobacz Praca nad pomijaniem zdarzeń przez kontrolki .

Zdarzenia "wersja zapoznawcza" (tunelowanie) a zdarzenia bubbling i obsługa zdarzeń

Zdarzenia kierowane w wersji zapoznawczej to zdarzenia, które podążają za trasą tunelowania przez drzewo elementów. "Podgląd" wyrażony w konwencji nazewnictwa wskazuje ogólną zasadę dla zdarzeń wejściowych, które podgląd (tunelowanie) kierowane zdarzenia są wywoływane przed równoważnym zdarzeniem kierowanym przez bubbling. Ponadto zdarzenia kierowane przez dane wejściowe, które mają parę tunelowania i bubbling mają odrębną logikę obsługi. Jeśli zdarzenie kierowane do tunelowania/podglądu jest oznaczone jako obsługiwane przez odbiornik zdarzeń, zdarzenie kierowane przez bubbling zostanie oznaczone jeszcze przed odebraniem go przez odbiorniki zdarzeń kierowanych. Zdarzenia kierowane przez tunelowanie i bubbling są technicznie oddzielnymi zdarzeniami, ale celowo współużytkują to samo wystąpienie danych zdarzeń, aby umożliwić to zachowanie.

Połączenie między tunelowaniem i rozsyłaniem kierowanych zdarzeń jest realizowane przez wewnętrzną implementację sposobu, w jaki dana klasa WPF podnosi własne zadeklarowane zdarzenia trasowane, i dotyczy to sparowanych zdarzeń kierowanych przez dane wejściowe. Jednak jeśli ta implementacja na poziomie klasy nie istnieje, nie ma połączenia między zdarzeniem kierowanym przez tunelowanie a zdarzeniem kierowanym przez kłęb, które współużytkują schemat nazewnictwa: bez takiej implementacji byłyby to dwa całkowicie oddzielne zdarzenia kierowane i nie byłyby wywoływane w sekwencji ani udostępniać danych zdarzeń.

Aby uzyskać więcej informacji na temat implementowania par zdarzeń kierowanych przez tunel/bąbelek w klasie niestandardowej, zobacz Create a Custom Routed Event (Tworzenie niestandardowego zdarzenia trasowanego).

Programy obsługi klas i programy obsługi wystąpień

Zdarzenia kierowane uwzględniają dwa różne typy odbiorników do zdarzenia: odbiorniki klas i odbiorniki wystąpień. Odbiorniki klas istnieją, ponieważ typy nazwały określony EventManager interfejs APIRegisterClassHandler , w ich konstruktorze statycznym lub zastąpiły wirtualną metodę obsługi klas z klasy bazowej elementu. Odbiorniki wystąpień to określone wystąpienia klasy/elementy, w których co najmniej jeden program obsługi został dołączony do tego zdarzenia kierowanego przez wywołanie metody AddHandler. Istniejące zdarzenia kierowane przez platformę WPF tworzą wywołania AddHandler w ramach otoki zdarzeń środowiska uruchomieniowego języka wspólnego (CLR) i{} usuwają{} implementacje zdarzenia, co jest również sposobem włączenia prostego mechanizmu XAML dołączania programów obsługi zdarzeń za pośrednictwem składni atrybutów. W związku z tym nawet proste użycie XAML ostatecznie odpowiada wywołaniu AddHandler .

Elementy w drzewie wizualnym są sprawdzane pod kątem zarejestrowanych implementacji programu obsługi. Programy obsługi są potencjalnie wywoływane w całej trasie w kolejności, która jest nieodłączną częścią typu strategii routingu dla tego zdarzenia kierowanego. Na przykład zdarzenia kierowane przez bubbling najpierw wywołają te programy obsługi, które są dołączone do tego samego elementu, który wywołał zdarzenie kierowane. Następnie przekierowane zdarzenie "bąbelki" do następnego elementu nadrzędnego i tak dalej, dopóki element główny aplikacji nie zostanie osiągnięty.

Z punktu widzenia elementu głównego w trasie bubbling, jeśli obsługa klas lub dowolny element bliżej źródła kierowanego zdarzenia wywołać programy obsługi, które oznaczają argumenty zdarzeń jako obsługiwane, programy obsługi elementów głównych nie są wywoływane, a trasa zdarzeń jest skutecznie skracana przed dotarciem do tego elementu głównego. Jednak trasa nie jest całkowicie zatrzymana, ponieważ programy obsługi można dodać przy użyciu specjalnego warunkowego, który powinien być nadal wywoływany, nawet jeśli program obsługi klas lub program obsługi wystąpień oznaczył zdarzenie kierowane jako obsługiwane. Wyjaśniono to w artykule Dodawanie programów obsługi wystąpień, które są wywoływane nawet wtedy, gdy zdarzenia są oznaczone jako obsługiwane, w dalszej części tego tematu.

Na wyższym poziomie niż trasa zdarzeń istnieje również potencjalnie wiele procedur obsługi klas działających na dowolnym wystąpieniu klasy. Dzieje się tak, ponieważ model obsługi klas dla zdarzeń kierowanych umożliwia wszystkim możliwym klasom w hierarchii klas rejestrowanie własnego programu obsługi klas dla każdego kierowanego zdarzenia. Każda procedura obsługi klas jest dodawana do magazynu wewnętrznego, a po utworzeniu trasy zdarzeń dla aplikacji wszystkie procedury obsługi klas są dodawane do trasy zdarzeń. Procedury obsługi klas są dodawane do trasy, tak aby najbardziej pochodna procedura obsługi klas jest wywoływana jako pierwsza, a procedury obsługi klas z każdej kolejnej klasy bazowej są wywoływane w następnej kolejności. Ogólnie rzecz biorąc, programy obsługi klas nie są zarejestrowane, tak aby reagowały również na kierowane zdarzenia, które zostały już oznaczone jako obsługiwane. W związku z tym ten mechanizm obsługi klas umożliwia jedną z dwóch opcji:

  • Klasy pochodne mogą uzupełniać obsługę klas dziedziczone po klasie bazowej, dodając procedurę obsługi, która nie oznacza obsługiwanego zdarzenia trasowanego, ponieważ program obsługi klas bazowych zostanie wywołany czasami po procedurze obsługi klas pochodnych.

  • Klasy pochodne mogą zastąpić obsługę klas z klasy bazowej, dodając procedurę obsługi klas, która oznacza obsłużone zdarzenie trasowane. Należy zachować ostrożność przy użyciu tego podejścia, ponieważ potencjalnie zmieni ona zamierzony projekt kontroli podstawowej w takich obszarach jak wygląd wizualizacji, logika stanu, obsługa danych wejściowych i obsługa poleceń.

Obsługa klas kierowanych zdarzeń według klas bazowych kontrolek

W każdym węźle danego elementu w trasie zdarzeń odbiorniki klas mają możliwość reagowania na zdarzenie kierowane przed każdym odbiornikiem wystąpienia w elemecie może. Z tego powodu programy obsługi klas są czasami używane do pomijania zdarzeń kierowanych, których konkretna implementacja klasy kontrolnej nie chce dalej propagować ani zapewnić specjalnej obsługi tego zdarzenia kierowanego, który jest funkcją klasy. Na przykład klasa może zgłosić własne zdarzenie specyficzne dla klasy, które zawiera bardziej szczegółowe informacje o tym, co oznacza warunek wejściowy użytkownika w kontekście tej konkretnej klasy. Implementacja klasy może następnie oznaczać bardziej ogólne zdarzenie kierowane zgodnie z procedurą obsługi. Programy obsługi klas są zwykle dodawane tak, że nie są wywoływane dla zdarzeń kierowanych, w których udostępnione dane zdarzeń zostały już oznaczone jako obsługiwane, ale w przypadku nietypowych przypadków istnieje również RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) podpis rejestrujący programy obsługi klas do wywoływania nawet wtedy, gdy obsługiwane są zdarzenia kierowane.

Virtuals programu obsługi klas

Niektóre elementy, szczególnie podstawowe elementy, takie jak UIElement, uwidaczniają puste metody wirtualne "On*Event" i "OnPreview*Event", które odpowiadają ich liście publicznych zdarzeń kierowanych. Te metody wirtualne można zastąpić, aby zaimplementować procedurę obsługi klas dla tego zdarzenia kierowanego. Klasy elementów podstawowych rejestrują te metody wirtualne jako procedurę obsługi klas dla każdego takiego zdarzenia kierowanego przy użyciu zgodnie z RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) wcześniejszym opisem. Metody wirtualne On*Event sprawiają, że znacznie prostsze jest zaimplementowanie obsługi klas dla odpowiednich zdarzeń kierowanych bez konieczności specjalnej inicjalizacji w konstruktorach statycznych dla każdego typu. Na przykład można dodać obsługę klas dla DragEnter zdarzenia w dowolnej UIElement klasie pochodnej, przesłaniając metodę wirtualną OnDragEnter . W ramach przesłonięcia można obsługiwać zdarzenie kierowane, zgłaszać inne zdarzenia, inicjować logikę specyficzną dla klasy, która może zmieniać właściwości elementu w wystąpieniach lub dowolną kombinację tych akcji. Zazwyczaj należy wywołać implementację podstawową w takich przesłonięciach, nawet jeśli oznaczysz obsłużone zdarzenie. Wywoływanie implementacji podstawowej jest zdecydowanie zalecane, ponieważ metoda wirtualna znajduje się w klasie bazowej. Standardowy wzorzec wirtualny chronionych przez wywołanie implementacji bazowych z każdego praktycznie zasadniczo zastępuje i równoległy podobny mechanizm natywny dla obsługi klasy zdarzeń kierowanych, gdzie programy obsługi klas dla wszystkich klas w hierarchii klas są wywoływane w dowolnym wystąpieniu, począwszy od programu obsługi klasy pochodnej i kontynuowania obsługi klas bazowych. Należy pominąć wywołanie implementacji podstawowej tylko wtedy, gdy klasa ma celowe wymaganie zmiany logiki obsługi klas bazowych. Niezależnie od tego, czy wywołasz implementację podstawową przed lub po zastąpieniu kodu, będzie zależeć od charakteru implementacji.

Obsługa klas zdarzeń wejściowych

Metody wirtualne programu obsługi klas są rejestrowane w taki sposób, że są wywoływane tylko w przypadkach, gdy wszystkie udostępnione dane zdarzenia nie są jeszcze oznaczone jako obsługiwane. Ponadto w przypadku zdarzeń wejściowych unikatowo wersje tunelowania i bubbling są zwykle wywoływane w sekwencji i udostępniają dane zdarzenia. Wiąże się to z tym, że dla danej pary programów obsługi klas zdarzeń wejściowych, w których jedna jest wersją tunelowania, a druga jest wersją bubbling, możesz nie chcieć oznaczyć zdarzenia obsługiwane natychmiast. Jeśli zaimplementujesz metodę wirtualną obsługi klasy tunelowania w celu oznaczenia obsługiwanego zdarzenia, uniemożliwi to wywoływanie programu obsługi klasy bubbling (a także zapobieganie wywoływaniu jakichkolwiek zwykle zarejestrowanych programów obsługi wystąpień dla wywoływanego zdarzenia tunelowania lub bubbling).

Po zakończeniu obsługi klas w węźle są brane pod uwagę odbiorniki wystąpień.

Dodawanie programów obsługi wystąpień, które są wywoływane nawet wtedy, gdy zdarzenia są oznaczone jako obsługiwane

Metoda AddHandler dostarcza określone przeciążenie, które umożliwia dodawanie programów obsługi, które będą wywoływane przez system zdarzeń za każdym razem, gdy zdarzenie dociera do elementu obsługi w trasie, nawet jeśli niektóre inne procedury obsługi zostały już dostosowane dane zdarzeń, aby oznaczyć to zdarzenie jako obsługiwane. Zazwyczaj nie jest to wykonywane. Ogólnie rzecz biorąc, programy obsługi można zapisywać w celu dostosowania wszystkich obszarów kodu aplikacji, które mogą mieć wpływ na zdarzenie, niezależnie od tego, gdzie było obsługiwane w drzewie elementów, nawet jeśli wymagane jest wiele wyników końcowych. Ponadto zazwyczaj istnieje tylko jeden element, który musi odpowiadać na to zdarzenie, a odpowiednia logika aplikacji już się wydarzyła. handledEventsToo Jednak przeciążenie jest dostępne w wyjątkowych przypadkach, gdy jakiś inny element w drzewie elementów lub komponowaniu kontrolek oznaczył zdarzenie jako obsługiwane, ale inne elementy albo wyższe lub niższe w drzewie elementów (w zależności od trasy) nadal chcą wywołać własne programy obsługi.

Kiedy oznaczyć zdarzenia obsługiwane jako nieobsługiwane

Ogólnie rzecz biorąc, zdarzenia kierowane oznaczone jako obsługiwane nie powinny być oznaczone nieobsługiwane (Handled ustawione z powrotem na false) nawet przez programy obsługi, które działają w dniu handledEventsToo. Jednak niektóre zdarzenia wejściowe mają reprezentacje zdarzeń wysokiego i niższego poziomu, które mogą nakładać się, gdy zdarzenie wysokiego poziomu jest widoczne na jednej pozycji w drzewie i zdarzeń niskiego poziomu w innej pozycji. Rozważmy na przykład przypadek, w którym element podrzędny nasłuchuje zdarzenia klucza wysokiego poziomu, na przykład TextInput gdy element nadrzędny nasłuchuje zdarzenia niskiego poziomu, takiego jak KeyDown. Jeśli element nadrzędny obsługuje zdarzenie niskiego poziomu, zdarzenie wyższego poziomu można pominąć nawet w elemecie podrzędnym, który intuicyjnie powinien mieć możliwość obsługi zdarzenia.

W takich sytuacjach może być konieczne dodanie programów obsługi do elementów nadrzędnych i elementów podrzędnych dla zdarzenia niskiego poziomu. Implementacja programu obsługi elementów podrzędnych może oznaczać zdarzenie niskiego poziomu zgodnie z obsługą, ale implementacja programu obsługi elementów nadrzędnych ponownie ustawiłaby go nieobsługiwane, tak aby kolejne elementy drzewa (a także zdarzenia wysokiego poziomu) mogły odpowiedzieć. Ta sytuacja powinna być dość rzadka.

Celowo pomijanie zdarzeń wejściowych na potrzeby komposiowania kontrolek

Głównym scenariuszem, w którym jest używana obsługa klas kierowanych zdarzeń, jest obsługa zdarzeń wejściowych i złożonych kontrolek. Kontrolka złożona składa się z wielu praktycznych kontrolek lub klas bazowych kontrolek. Często autor kontrolki chce amalgamate wszystkie możliwe zdarzenia wejściowe, które każdy z podskładników może zgłosić, aby zgłosić całą kontrolkę jako pojedyncze źródło zdarzeń. W niektórych przypadkach autor kontrolki może chcieć całkowicie pominąć zdarzenia ze składników lub zastąpić zdarzenie zdefiniowane przez składnik, które niesie więcej informacji lub sugeruje bardziej szczegółowe zachowanie. Przykład kanoniczny, który jest natychmiast widoczny dla dowolnego autora składnika, to sposób, w jaki program Windows Presentation Foundation (WPF) Button obsługuje dowolne zdarzenie myszy, które ostatecznie rozwiąże intuicyjne zdarzenie, które ma wszystkie przyciski: Click zdarzenie.

Klasa Button bazowa (ButtonBase) pochodzi z Control klasy , z której z kolei pochodzi element FrameworkElement i UIElement, a znaczna część infrastruktury zdarzeń potrzebnej do kontroli przetwarzania danych wejściowych jest dostępna na UIElement poziomie. W szczególności przetwarza zdarzenia ogólne Mouse obsługujące UIElement testowanie trafień kursora myszy w granicach i udostępniają różne zdarzenia dla najpopularniejszych akcji przycisków, takich jak MouseLeftButtonDown. UIElement Udostępnia również pustą wirtualną pustą maszynę wirtualną OnMouseLeftButtonDown jako wstępnie wyrejestrowaną procedurę obsługi klas dla MouseLeftButtonDownelementu i ButtonBase zastępuje ją. ButtonBase Podobnie program używa procedur obsługi klas dla klasy MouseLeftButtonUp. W przesłonięciach, które są przekazywane dane zdarzenia, implementacje oznaczają to RoutedEventArgs wystąpienie jako obsługiwane przez ustawienie Handled na true, a te same dane zdarzenia są kontynuowane wzdłuż pozostałej części trasy do innych procedur obsługi klas, a także do programów obsługi wystąpień lub zestawów zdarzeń. OnMouseLeftButtonUp Ponadto przesłonięcia będą następnie zgłaszać Click zdarzenie. Wynikiem końcowym dla większości odbiorników będzie to, że MouseLeftButtonDown zdarzenia i MouseLeftButtonUp "znikają" i są zastępowane przez Click, zdarzenie, które zawiera więcej znaczenia, ponieważ wiadomo, że to zdarzenie pochodzi z prawdziwego przycisku, a nie części złożonej przycisku lub innego elementu całkowicie.

Obejście pomijania zdarzeń według kontrolek

Czasami to zachowanie pomijania zdarzeń w ramach poszczególnych kontrolek może zakłócać niektóre bardziej ogólne intencje logiki obsługi zdarzeń dla aplikacji. Na przykład jeśli z jakiegoś powodu aplikacja miała program obsługi MouseLeftButtonDown znajdujący się w elemecie głównym aplikacji, zauważysz, że każde kliknięcie myszy na przycisku nie wywołuje ani nie będzie wywoływać MouseLeftButtonDown procedur MouseLeftButtonUp obsługi na poziomie głównym. Samo zdarzenie rzeczywiście nie bąbelkowe (ponownie trasy zdarzeń nie są naprawdę zakończone, ale kierowany system zdarzeń zmienia zachowanie wywołania procedury obsługi po oznaczeniu obsłużone). Gdy zdarzenie kierowane dotarło do przycisku, obsługa klasy oznaczyła MouseLeftButtonDown obsłużoną obsługę, ButtonBase ponieważ chciała zastąpić Click zdarzenie bardziej znaczącym. W związku z tym żadna standardowa MouseLeftButtonDown procedura obsługi dalej trasy nie zostanie wywołana. Istnieją dwie techniki, których można użyć, aby upewnić się, że programy obsługi będą wywoływane w tej sytuacji.

Pierwszą techniką jest celowe dodanie procedury obsługi przy użyciu handledEventsToo podpisu AddHandler(RoutedEvent, Delegate, Boolean). Ograniczeniem tego podejścia jest to, że ta technika dołączania programu obsługi zdarzeń jest możliwa tylko z kodu, a nie z znaczników. Prosta składnia określania nazwy procedury obsługi zdarzeń jako wartości atrybutu zdarzenia za pośrednictwem języka XAML (Extensible Application Markup Language) nie włącza tego zachowania.

Druga technika działa tylko w przypadku zdarzeń wejściowych, w których są sparowane wersje tunelowania i bubbling zdarzenia kierowanego. W przypadku tych zdarzeń kierowanych można zamiast tego dodać programy obsługi do równoważnego zdarzenia kierowanego w wersji zapoznawczej/tunelowania. To zdarzenie kierowane będzie tunelowane przez trasę rozpoczynającą się od katalogu głównego, więc kod obsługi klas przycisków nie przechwytuje go, wstępnie dołączając procedurę obsługi podglądu na poziomie elementu przodka w drzewie elementów aplikacji. Jeśli używasz tego podejścia, należy zachować ostrożność podczas oznaczania dowolnego obsłużonego zdarzenia w wersji zapoznawczej. W przykładzie podanym przy PreviewMouseLeftButtonDown obsłudze elementu głównego, jeśli zdarzenie zostało oznaczone jako Handled w implementacji programu obsługi, zdarzenie zostanie pominięte Click . Zazwyczaj nie jest to pożądane zachowanie.

Zobacz też