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

Procedury obsługi dla zdarzenia kierowanego mogą oznaczać zdarzenie obsługiwane w danych zdarzenia. Obsługa zdarzenia w praktyce spowoduje skrócenie trasy. Obsługa klas jest koncepcją programowania obsługiwaną przez rozesłane zdarzenia. Obsługa klasy ma możliwość obsługi określonego zdarzenia kierowanego na poziomie klasy z obsługą, która jest wywoływana przed jakimkolwiek wystąpieniem dowolnego wystąpienia klasy.

Wymagania wstępne

W tym temacie opisano koncepcje wprowadzone w omówieniu zdarzeń kierowanych.

Kiedy należy oznaczyć zdarzenia jako obsłużone

Po ustawieniu wartości Handled właściwości na wartość true w danych zdarzenia dla zdarzenia kierowanego jest to określane jako "Oznaczanie zdarzenia obsłużonego". Nie istnieje bezwzględna reguła dla sytuacji, gdy należy oznaczyć zdarzenia kierowane jako obsługiwane przez autora aplikacji albo jako autora kontrolki, który reaguje na istniejące zdarzenia kierowane lub implementuje nowe zdarzenia kierowane. W większości przypadków pojęcie "obsłużone" zgodnie z danymi zdarzeń zdarzenia kierowanego zdarzenia powinno być używane jako ograniczony protokół dla odpowiedzi aplikacji na różne zdarzenia kierowane w WPF interfejsie API, a także dla wszystkich niestandardowych zdarzeń kierowanych. Innym sposobem rozważenia problemu "obsłużonego" jest to, że zwykle należy oznaczyć zdarzenie kierowane, które jest obsługiwane, jeśli kod odpowiedział na zdarzenie kierowane w znaczący i stosunkowo kompletny sposób. Zwykle nie powinna istnieć więcej niż jedna znacząca odpowiedź, która wymaga oddzielnych implementacji obsługi dla dowolnego wystąpienia zdarzenia pojedynczego rozesłania. Jeśli potrzebne są więcej odpowiedzi, należy wdrożyć wymagany kod za pośrednictwem logiki aplikacji, która jest łańcuchem w ramach jednego programu obsługi, a nie przy użyciu systemu zdarzeń kierowanych do przekazywania. Pojęcie "znaczące" jest również subiektywne i zależy od aplikacji lub kodu. Ogólnie rzecz biorąc, niektóre "znaczące odpowiedzi" obejmują: Ustawianie fokusu, modyfikowanie stanu publicznego, Ustawianie właściwości, które mają wpływ na reprezentację wizualną i wywoływanie innych nowych zdarzeń. Przykładami nieznaczących odpowiedzi są: modyfikowanie stanu prywatnego (bez wpływu na wizualizacje lub reprezentacja programowa), rejestrowanie zdarzeń lub sprawdzanie argumentów zdarzenia i wybór braku odpowiedzi na nie.

Zachowanie systemu zdarzenia kierowanego wzmacnia ten model "znaczna odpowiedź" dla użycia obsługiwanego stanu zdarzenia kierowanego, ponieważ programy obsługi dodane w XAML lub typowy podpis AddHandler nie są wywoływane w odpowiedzi na zdarzenie kierowane, w którym dane zdarzenia są już oznaczone jako obsługiwane. handledEventsToo AddHandler(RoutedEvent, Delegate, Boolean) Aby obsłużyć zdarzenia kierowane, które są oznaczone jako obsługiwane przez wcześniejszych uczestników trasy zdarzeń, należy przejść przez dodatkowy nakład pracy.

W niektórych okolicznościach formanty same oznaczą niektóre zdarzenia kierowane jako obsługiwane. Obsłużone zdarzenie kierowane reprezentuje decyzję przez WPF autorów kontroli, że akcje kontrolki w odpowiedzi na zdarzenie kierowane są istotne lub kompletne w ramach implementacji kontroli, a zdarzenie nie wymaga dalszej obsługi. Zwykle jest to realizowane przez dodanie obsługi klasy dla zdarzenia lub przez zastąpienie jednej z wirtualnych elementów obsługi klasy istniejących w klasie bazowej. W razie potrzeby można nadal obejść tę obsługę zdarzeń; Zobacz Praca z pomijaniem zdarzeń przez kontrolki w dalszej części tego tematu.

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

Zdarzenia kierowane do wersji zapoznawczej to zdarzenia, które obserwują trasy tunelowania za pomocą drzewa elementów. "Wersja zapoznawcza" wyrażona w konwencji nazewnictwa jest wskazówką ogólnej zasady dla zdarzeń wejściowych, które są zgłaszane przez zdarzenia kierowane (tunelowanie), które są wywoływane przed równoważeniem zdarzenia propagacji. Ponadto w przypadku wejściowych zdarzeń, które mają parę tunelowanie i Propagacja, istnieją różne logiki obsługi. Jeśli zdarzenie Tunneling/Preview jest oznaczone jako obsługiwane przez odbiornik zdarzeń, zdarzenie propagacji kierowane zostanie oznaczone jako obsługiwane nawet przed odebraniem przez nie wszystkich odbiorników zdarzenia propagacji. Zdarzenia tunelowania i propagacji kierowane są technicznie oddzielnymi zdarzeniami, ale świadomie współużytkują to samo wystąpienie danych zdarzeń, aby umożliwić takie zachowanie.

Połączenie między zdarzeniami tunelowania i propagacji jest realizowane przez wewnętrzną implementację podanej WPF klasy, która wywołuje własne zadeklarowane zdarzenia kierowane, i jest to prawdziwe dla sparowanych zdarzeń wejściowych. Ale jeśli nie istnieje taka implementacja na poziomie klasy, nie ma połączenia między zdarzeniem rozsyłania tunelowania i propagacją zdarzeń kierowanych, które współużytkują schemat nazewnictwa: bez takiej implementacji, byłyby dwa całkowicie oddzielone zdarzenia kierowane i nie zostaną zgłoszone w sekwencji lub udostępnione dane zdarzenia.

Aby uzyskać więcej informacji na temat implementowania par zdarzeń danych wejściowych tunelu/bąbelków w klasie niestandardowej, zobacz Tworzenie niestandardowego zdarzenia kierowanego.

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

Zdarzenia kierowane uwzględniają dwa różne typy odbiorników dla zdarzenia: detektory klas i detektory wystąpień. Odbiorniki klas istnieją, ponieważ typy wywołały konkretny EventManager interfejs API, RegisterClassHandler , w konstruktorze statycznym lub przesłonili metodę wirtualną procedury obsługi klasy z klasy bazowej elementu. Detektory wystąpień są określonymi wystąpieniami klasy/elementami, w których co najmniej jeden program obsługi został dołączony do tego zdarzenia przez wywołanie AddHandler . Istniejące WPF zdarzenia kierowane powodują wywołania AddHandler jako część otoki zdarzeń środowiska uruchomieniowego języka wspólnego (CLR) Dodaj {} i Usuń {} implementacje zdarzenia, co jest również sposobem prostego XAML mechanizmu dołączania obsługi zdarzeń za pomocą składni atrybutów. W związku z tym nawet proste XAML użycie jest ostatecznie równe AddHandler wywołaniu.

Elementy w drzewie wizualnym są sprawdzane pod kątem zastrzeżonych implementacji programu obsługi. Procedury obsługi są potencjalnie wywoływane w całej trasie, w kolejności, która jest nieodłączna w ramach strategii routingu dla tego zdarzenia. Na przykład Propagacja zdarzeń kierowanych najpierw wywoła te procedury obsługi, które są dołączone do tego samego elementu, który wywołał zdarzenie kierowane. Następnie rozesłane zdarzenie "bąbelki" do następnego elementu nadrzędnego i tak dalej, aż do osiągnięcia elementu głównego aplikacji.

Z punktu widzenia elementu głównego w trasie propagacji, jeśli obsługa klasy lub dowolna elementu bliżej źródła zdarzeń rozsyłanych wywołała zdarzenia, które oznaczają argumenty zdarzeń jako obsługiwane, a następnie programy obsługi dla elementów głównych nie są wywoływane, a trasa zdarzenia jest efektywnie skracana przed osiągnięciem tego elementu głównego. Trasa nie jest jednak całkowicie zatrzymana, ponieważ programy obsługi można dodać przy użyciu warunku specjalnego, że powinny być nadal wywoływane, nawet jeśli procedura obsługi klasy lub program obsługi wystąpienia oznaczył zdarzenia kierowane jako obsłużone. Jest to wyjaśnione w temacie 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 zdarzenia istnieje również możliwość obsługi wielu klas, które działają na dowolnym wystąpieniu klasy. Jest to spowodowane tym, że model obsługi klasy dla zdarzeń kierowanych umożliwia wszystkim klasom w hierarchii klas każdemu zarejestrowaniu własnej obsługi klasy dla każdego zdarzenia kierowanego. Każda procedura obsługi klas jest dodawana do magazynu wewnętrznego. Po skonstruowaniu trasy zdarzenia dla aplikacji programy obsługi klas są dodawane do trasy zdarzenia. Procedury obsługi klas są dodawane do trasy w taki sposób, aby procedura obsługi klasy najbardziej pochodnej była wywoływana jako pierwsza, a procedury obsługi klas z każdej kolejnej klasy bazowej są wywoływane dalej. Ogólnie rzecz biorąc, programy obsługi klas nie są zarejestrowane w taki sposób, że reagują również na zdarzenia kierowane, które zostały już oznaczone jako obsługiwane. W związku z tym mechanizm obsługi klasy umożliwia wybranie jednej z dwóch opcji:

  • Klasy pochodne mogą uzupełniać obsługę klasy, która jest dziedziczona z klasy podstawowej przez dodanie programu obsługi, który nie oznacza obsłużonego zdarzenia kierowanego, ponieważ procedura obsługi klasy bazowej zostanie wywołana jakiś czas po obsłudze klasy pochodnej.

  • Klasy pochodne mogą zastąpić obsługę klasy z klasy podstawowej przez dodanie obsługi klasy, która oznacza obsłużone zdarzenie kierowane. Należy zachować ostrożność w tym podejściu, ponieważ może to spowodować zmianę zamierzonego projektu kontrolki podstawowej w obszarach takich jak wygląd wizualizacji, logika stanu, obsługa danych wejściowych i obsługa poleceń.

Obsługa klasy zdarzeń kierowanych przez klasy bazowe kontroli

W każdym z podanym węzłem elementu w marszrucie zdarzeń detektory klas mają możliwość odpowiadania na zdarzenia kierowane przed dowolnym odbiornikiem wystąpienia elementu. Z tego powodu procedury obsługi klas są czasami używane do pomijania zdarzeń kierowanych, których dana implementacja klasy kontroli nie chce bardziej propagować ani w celu zapewnienia specjalnej obsługi tego zdarzenia, które 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 dany warunek wejściowy użytkownika wskazuje w kontekście tej konkretnej klasy. Implementacja klasy może następnie oznaczyć bardziej ogólne zdarzenie routingu jako obsłużone. Procedury obsługi klas są zazwyczaj dodawane, tak że nie są wywoływane dla zdarzeń kierowanych, w przypadku których dane zdarzenia udostępnionego zostały już oznaczone jako obsłużone, ale w przypadku nietypowych przypadków istnieje również RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) sygnatura, która rejestruje procedury obsługi do wywołania nawet wtedy, gdy zdarzenia kierowane są oznaczone jako obsługiwane.

Wirtualne procedury obsługi klas

Niektóre elementy, szczególnie elementy podstawowe, takie jak UIElement , uwidaczniają metody wirtualne "on * Event" i "onpreview * Event", które odpowiadają ich liście publicznych tras routingu. Te metody wirtualne można zastąpić, aby zaimplementować obsługę klasy dla tego zdarzenia. Klasy elementów podstawowych rejestrują te metody wirtualne jako obsługę klas dla każdego takiego zdarzenia kierowanego RegisterClassHandler(Type, RoutedEvent, Delegate, Boolean) , zgodnie z wcześniejszym opisem. W * przypadku takich zdarzeń wirtualne metody są znacznie prostsze do implementowania 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ę klasy dla DragEnter zdarzenia w dowolnej UIElement klasie pochodnej przez zastąpienie OnDragEnter metody wirtualnej. W ramach przesłonięcia można obsłużyć zdarzenie trasowane, zgłosić inne zdarzenia, zainicjować logikę specyficzną dla klasy, która może zmienić właściwości elementu w wystąpieniach lub dowolną kombinację tych akcji. Na ogół należy wywołać implementację podstawową w takich zastąpień, nawet jeśli zostanie zaznaczone zdarzenie obsłużone. Wywołanie podstawowej implementacji jest zdecydowanie zalecane, ponieważ metoda wirtualna znajduje się w klasie bazowej. Standardowy, chroniony wzorzec wirtualny wywołujący implementacje podstawowe z poszczególnych wirtualnych, zasadniczo zastępuje i równoległy do podobnego mechanizmu, który jest natywny dla kierowania klas zdarzeń, zgodnie z którym procedury obsługi klas dla wszystkich klas w hierarchii klas są wywoływane w dowolnym wystąpieniu, rozpoczynając od procedury obsługi klasy pochodnej i kontynuując obsługę klasy podstawowej. Należy pominąć podstawowe wywołanie implementacji tylko wtedy, gdy Klasa ma zamierzone wymaganie zmiany logiki obsługi klasy podstawowej. Niezależnie od tego, czy zostanie wywołana implementacja podstawowa przed czy po zastępujący kod zależą od charakteru implementacji.

Obsługa klasy zdarzeń wejściowych

Metody wirtualne procedury obsługi klas są rejestrowane w taki sposób, że są wywoływane tylko w przypadkach, gdy żadne udostępnione dane zdarzenia nie są już oznaczone jako obsługiwane. Ponadto dla zdarzeń wejściowych w unikatowy sposób tunelowanie i wersje propagacji są zwykle wywoływane w kolejności i udostępniania danych zdarzeń. Pociąga to za sobą, że dla danej pary programów obsługi klasy zdarzeń wejściowych, gdzie jedna jest wersją tunelowania, a druga jest wersją propagacji, można nie chcieć oznaczyć zdarzenia natychmiast. W przypadku zaimplementowania klasy tunelowania obsługującej metodę wirtualną w celu oznaczenia obsłużonego zdarzenia, co uniemożliwi wywoływanie procedury obsługi klasy propagacji (a także zapobiega wywoływaniu przez nie wszystkich normalnie zarejestrowanych programów obsługi wystąpień dla tunelowania lub zdarzenia propagacji).

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

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

AddHandlerMetoda dostarcza konkretne Przeciążenie, które pozwala na dodawanie programów obsługi, które będą wywoływane przez system zdarzeń za każdym razem, gdy zdarzenie osiągnie element obsługi w marszrucie, nawet jeśli inna procedura obsługi już dostosowuje dane zdarzenia, aby oznaczyć to zdarzenie jako obsługiwane. Nie jest to zwykle wykonywane. Ogólnie rzecz biorąc, programy obsługi można napisać, aby dostosować wszystkie obszary kodu aplikacji, które mogą mieć wpływ na zdarzenie, bez względu na to, gdzie zostało obsłużone w drzewie elementów, nawet jeśli pożądane jest wiele wyników końcowych. Ponadto zazwyczaj istnieje tylko jeden element, który musi odpowiedzieć na to zdarzenie, a odpowiednia logika aplikacji już się zakończyła. Jednak handledEventsToo Przeciążenie jest dostępne dla przypadków wyjątkowych, w których jakiś inny element w drzewie elementów lub w składowej kontrolki już oznaczył zdarzenie jako obsłużone, ale inne elementy albo wyższe lub niższe w drzewie elementów (w zależności od trasy) nadal chcą mieć własne procedury obsługi.

Kiedy należy oznaczyć zdarzenia obsłużone jako nieobsłużone

Na ogół zdarzenia kierowane, które są oznaczone jako obsługiwane, nie powinny być oznaczone jako nieobsłużone ( Handled ustawienie wstecz do false ), nawet przez programy obsługi, które działają handledEventsToo . Jednak niektóre zdarzenia wejściowe zawierają reprezentacje zdarzeń wysokiego poziomu i niższego poziomu, które mogą się nakładać w przypadku wystąpienia zdarzenia wysokiego poziomu w drzewie oraz zdarzenia niskiego poziomu na innym miejscu. Na przykład rozważmy przypadek, w którym element podrzędny nasłuchuje zdarzenia klucza wysokiego poziomu, takiego jak TextInput while, 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 elemencie podrzędnym, który intuicyjnie powinien mieć pierwszą okazję do obsłużenia zdarzenia.

W takich sytuacjach może być konieczne dodanie programów obsługi zarówno do elementów nadrzędnych, jak i elementów podrzędnych dla zdarzenia niskiego poziomu. Implementacja obsługi elementu podrzędnego może oznaczyć zdarzenie niskiego poziomu jako obsłużone, ale implementacja obsługi elementu nadrzędnego ustawi ją nieobsłużoną ponownie, aby dalsze elementy drzewa (jak również zdarzenie wysokiego poziomu) mogły odpowiadać. Ta sytuacja powinna być dość rzadki.

Świadome pomijanie zdarzeń wejściowych dla składowych kontroli

Głównym scenariuszem, w którym stosowana jest Klasa obsługi zdarzeń kierowanych, jest dla zdarzeń wejściowych i kontrolek złożonych. Złożona kontrolka jest według definicji składającej się z wielu praktycznych formantów lub klas podstawowych kontroli. Często autor formantu chce amalgamate wszystkie możliwe zdarzenia wejściowe, które mogą zostać zgłoszone przez każdy podskładniki, aby zgłosić cały formant jako pojedyncze Źródło zdarzenia. 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 zawiera więcej informacji, lub oznacza bardziej szczegółowe zachowanie. Przykład kanoniczny, który jest natychmiast widoczny dla każdego autora składnika, polega na tym, jak Windows Presentation Foundation (WPF) Button obsługuje zdarzenie myszy, które ostatecznie rozwiąże się z intuicyjnym zdarzeniem, które ma wszystkie przyciski: Click zdarzenie.

ButtonKlasa bazowa ( ButtonBase ) pochodzi z, która z kolei jest pochodząca Control z FrameworkElement i UIElement , i większość infrastruktury zdarzeń wymaganych do przetwarzania danych wejściowych kontroli jest dostępna na UIElement poziomie. W szczególności UIElement przetwarza ogólne Mouse zdarzenia obsługujące testowanie trafień kursora myszy w granicach, a także udostępnia odrębne zdarzenia dla najbardziej typowych akcji przycisków, takich jak MouseLeftButtonDown . UIElement Program udostępnia także pustą wirtualną OnMouseLeftButtonDown jako prezarejestrowaną procedurę obsługi klasy dla MouseLeftButtonDown i ButtonBase zastępuje ją. Podobnie, program ButtonBase używa obsługi klas dla MouseLeftButtonUp . W przesłonięciach, które są przekazywać dane zdarzenia, implementacje oznaczają, że to RoutedEventArgs wystąpienie jest obsługiwane przez ustawienie Handled do true , a te same dane zdarzenia są kontynuowane wzdłuż reszty trasy do innych programów obsługi klasy, a także do obsługi wystąpień lub metod ustawiających zdarzenia. Ponadto OnMouseLeftButtonUp przesłonięcie będzie dalej podnieść Click zdarzenie. Wynik końcowy dla większości detektorów będzie mieć miejsce, MouseLeftButtonDown gdy MouseLeftButtonUp zdarzenia i "znikają" i zostaną zastąpione zamiast Click , zdarzenie, które ma więcej znaczenia, ponieważ wiadomo, że to zdarzenie pochodzi z przycisku prawda, a nie z niepewną częścią lub z innego elementu.

Praca nad pomijaniem zdarzeń przez kontrolki

Czasami takie zachowanie pominięcia zdarzeń w ramach poszczególnych kontrolek może kolidować z bardziej ogólnymi intencjami logiki obsługi zdarzeń aplikacji. Na przykład, jeśli z jakiegoś powodu aplikacja miała procedurę obsługi dla MouseLeftButtonDown elementu głównego aplikacji, należy zauważyć, że kliknięcie przycisku nie spowoduje wywołania MouseLeftButtonDown ani MouseLeftButtonUp obsługi na poziomie głównym. Samo zdarzenie w rzeczywistości zostało wykonane ponownie, a trasy zdarzeń nie zakończyły się naprawdę, ale system zdarzeń został zmieniony przez wywołanie procedury obsługi po oznaczeniu jako obsługiwanego. Gdy zdarzenie kierowane osiągnie przycisk, ButtonBase Obsługa klasy oznaczona jako MouseLeftButtonDown obsłużona, ponieważ życzy sobie, że zastąpi to Click zdarzenie bardziej sensem. W związku z tym, dowolna standardowa MouseLeftButtonDown procedura obsługi nie zostanie wywołana. Istnieją dwie techniki, których można użyć, aby upewnić się, że procedury obsługi byłyby wywołane w tej sytuacji.

Pierwszą techniką jest zamierzone dodanie procedury obsługi przy użyciu handledEventsToo sygnatury AddHandler(RoutedEvent, Delegate, Boolean) . Ograniczenie tej metody polega na tym, że ta technika dołączania obsługi zdarzeń jest możliwa tylko z kodu, a nie z znaczników. Prosta składnia określająca nazwę programu obsługi zdarzeń jako wartość atrybutu zdarzenia za pośrednictwem nie pozwala na takie Extensible Application Markup Language (XAML) zachowanie.

Druga technika działa tylko dla zdarzeń wejściowych, w przypadku których są sparowane tunelowanie i propagacje wersji zdarzenia kierowanego. W przypadku tych zdarzeń rozesłanych zamiast tego można dodać programy obsługi do odpowiedniego zdarzenia zapoznawczego/tunelowania. To zdarzenie jest kierowane przez trasę rozpoczynającą się od elementu głównego, więc Klasa Button obsługująca kod nie przechwytuje go, co zakłada, że dołączono procedurę obsługi podglądu na pewnym poziomie elementu nadrzędnego w drzewie elementów aplikacji. W przypadku korzystania z tej metody należy zachować ostrożność podczas oznaczania obsługi zdarzeń w wersji zapoznawczej. W przypadku podanego przykładu z PreviewMouseLeftButtonDown obsługą w elemencie głównym, jeśli oznaczono zdarzenie jako Handled w implementacji programu obsługi, można faktycznie pominąć Click zdarzenie. Zwykle nie jest to pożądane zachowanie.

Zobacz też