Przegląd zdarzeń trasowanych (WPF .NET)

Windows Presentation Foundation (WPF) deweloperzy aplikacji i autorzy składników mogą używać zdarzeń kierowanych do propagowania zdarzeń za pomocą drzewa elementów i wywoływać programy obsługi zdarzeń na wielu odbiornikach w drzewie. Te funkcje nie znajdują się w zdarzeniach środowiska uruchomieniowego języka wspólnego (CLR). Kilka zdarzeń WPF jest kierowanych zdarzeń, takich jak ButtonBase.Click. W tym artykule omówiono podstawowe pojęcia związane z zdarzeniami kierowanymi i przedstawiono wskazówki dotyczące tego, kiedy i jak reagować na kierowane zdarzenia.

Ważne

Dokumentacja przewodnika dla komputerów stacjonarnych dla platform .NET 6 i .NET 5 (w tym .NET Core 3.1) jest w budowie.

Wymagania wstępne

W tym artykule przyjęto założenie, że podstawowa wiedza na temat środowiska uruchomieniowego języka wspólnego (CLR), programowania obiektowego i sposobu, w jaki układ elementów WPF może być koncepcyjny jako drzewo. Aby postępować zgodnie z przykładami w tym artykule, warto zapoznać się z językiem XAML (Extensible Application Markup Language) i wiedzieć, jak pisać aplikacje WPF.

Co to jest zdarzenie kierowane?

Możesz rozważyć kierowanie zdarzeń z perspektywy funkcjonalnej lub implementacji:

  • Z perspektywy funkcjonalnej zdarzenie kierowane jest typem zdarzenia, które może wywoływać programy obsługi na wielu odbiornikach w drzewie elementów, a nie tylko w źródle zdarzeń. Odbiornik zdarzeń to element, w którym jest dołączana i wywoływana procedura obsługi zdarzeń. Źródło zdarzenia jest elementem lub obiektem, który pierwotnie zgłosił zdarzenie.

  • Z perspektywy implementacji zdarzenie kierowane jest zdarzeniem zarejestrowanym w systemie zdarzeń WPF, wspieranym przez wystąpienie RoutedEvent klasy i przetwarzanym przez system zdarzeń WPF. Zazwyczaj zdarzenie kierowane jest implementowane za pomocą zdarzenia CLR "otoka", aby włączyć dołączanie programów obsługi w języku XAML i w kodzie w taki sam sposób, jak zdarzenie CLR.

Aplikacje WPF zwykle zawierają wiele elementów, które zostały zadeklarowane w języku XAML lub utworzone w kodzie. Elementy aplikacji istnieją w drzewie elementów. W zależności od sposobu definiowania zdarzenia kierowanego, gdy zdarzenie jest wywoływane na elemecie źródłowym:

  • Bąbelki w górę przez drzewo elementów z elementu źródłowego do elementu głównego, który jest zazwyczaj stroną lub oknem.
  • Tunele w dół przez drzewo elementów z elementu głównego do elementu źródłowego.
  • Nie przechodzi przez drzewo elementów i występuje tylko w elemecie źródłowym.

Rozważ następujące drzewo elementów częściowych:

<Border Height="30" Width="200" BorderBrush="Gray" BorderThickness="1">
    <StackPanel Background="LightBlue" Orientation="Horizontal" Button.Click="YesNoCancelButton_Click">
        <Button Name="YesButton">Yes</Button>
        <Button Name="NoButton">No</Button>
        <Button Name="CancelButton">Cancel</Button>
    </StackPanel>
</Border>

Drzewo elementów jest renderowane w następujący sposób:

Yes, No, and Cancel buttons.

Każdy z trzech przycisków jest potencjalnym Click źródłem zdarzeń. Po kliknięciu jednego z przycisków program zgłasza Click zdarzenie, które jest bąbelkowe od przycisku do elementu głównego. Elementy Button i Border nie mają dołączonych procedur obsługi zdarzeń, ale StackPanel nie. Być może inne elementy wyższe w drzewie, które nie są wyświetlane, również mają Click dołączone programy obsługi zdarzeń. Click Gdy zdarzenie osiągnie StackPanel element, system zdarzeń WPF wywołuje YesNoCancelButton_Click program obsługi, który jest do niego dołączony. Trasa zdarzenia dla Click zdarzenia w przykładzie to: Button - ->StackPanel ->Border> kolejne elementy nadrzędne.

Uwaga

Element, który pierwotnie wywołał zdarzenie trasowane, jest identyfikowany jako RoutedEventArgs.Source element w parametrach programu obsługi zdarzeń. Odbiornik zdarzeń jest elementem, w którym program obsługi zdarzeń jest dołączony i wywoływany, i jest identyfikowany jako nadawca w parametrach programu obsługi zdarzeń.

Scenariusze najwyższego poziomu dla zdarzeń kierowanych

Poniżej przedstawiono niektóre scenariusze, które motywowały koncepcję zdarzenia kierowanego i odróżniają je od typowego zdarzenia CLR:

  • Kompozycja sterowania i hermetyzacja: Różne kontrolki w WPF mają bogaty model zawartości. Na przykład można umieścić obraz wewnątrz Buttonobiektu , który skutecznie rozszerza drzewo wizualne przycisku. Jednak dodany obraz nie może przerwać działania przycisku hit-test, który musi odpowiadać, gdy użytkownik kliknie piksele obrazu.

  • Pojedyncze punkty załączników programu obsługi: można zarejestrować procedurę obsługi dla zdarzenia każdego przycisku Click , ale przy użyciu zdarzeń kierowanych można dołączyć jedną procedurę obsługi, jak pokazano w poprzednim przykładzie XAML. Dzięki temu można zmienić drzewo elementów pod pojedynczą procedurą obsługi, taką jak dodawanie lub usuwanie większej liczby przycisków, bez konieczności rejestrowania Click zdarzeń każdego przycisku. Po wystąpieniu Click zdarzenia logika obsługi może określić, skąd pochodzi zdarzenie. Następująca procedura obsługi określona w wcześniej pokazanym drzewie elementów XAML zawiera następującą logikę:

    private void YesNoCancelButton_Click(object sender, RoutedEventArgs e)
    {
        FrameworkElement sourceFrameworkElement = e.Source as FrameworkElement;
        switch (sourceFrameworkElement.Name)
        {
            case "YesButton":
                // YesButton logic.
                break;
            case "NoButton":
                // NoButton logic.
                break;
            case "CancelButton":
                // CancelButton logic.
                break;
        }
        e.Handled = true;
    }
    
    Private Sub YesNoCancelButton_Click(sender As Object, e As RoutedEventArgs)
        Dim frameworkElementSource As FrameworkElement = TryCast(e.Source, FrameworkElement)
    
        Select Case frameworkElementSource.Name
            Case "YesButton"
                ' YesButton logic.
            Case "NoButton"
                ' NoButton logic.
            Case "CancelButton"
                ' CancelButton logic.
        End Select
    
        e.Handled = True
    End Sub
    
  • Obsługa klas: zdarzenia trasowane obsługują program obsługi zdarzeń klasy zdefiniowany w klasie. Programy obsługi klas obsługują zdarzenie przed wszystkimi procedurami obsługi wystąpień dla tego samego zdarzenia w dowolnym wystąpieniu klasy.

  • Odwoływanie się do zdarzenia bez odbicia: każde kierowane zdarzenie tworzy identyfikator pola, RoutedEvent aby zapewnić niezawodną technikę identyfikacji zdarzeń, która nie wymaga odbicia statycznego ani odbicia w czasie wykonywania w celu zidentyfikowania zdarzenia.

Jak są implementowane zdarzenia kierowane

Zdarzenie kierowane to zdarzenie zarejestrowane w systemie zdarzeń WPF, wspierane przez wystąpienie RoutedEvent klasy i przetwarzane przez system zdarzeń WPF. Wystąpienie RoutedEvent uzyskane z rejestracji jest zwykle przechowywane jako public static readonly element członkowski klasy, która ją zarejestrowała. Ta klasa jest określana jako klasa "właściciel" zdarzenia. Zazwyczaj zdarzenie kierowane implementuje identycznie nazwane zdarzenie CLR "otoka". Otoka zdarzeń CLR zawiera add metody i remove metody dostępu umożliwiające dołączanie programów obsługi w języku XAML i w kodzie za pośrednictwem składni zdarzeń specyficznych dla języka. Metody add i remove zastępują implementację środowiska CLR i wywołają kierowane zdarzenia AddHandler i RemoveHandler metody. Mechanizm tworzenia kopii zapasowych zdarzeń kierowanych i połączenia jest koncepcyjnie podobny do tego, w jaki sposób właściwość zależności jest właściwością CLR wspieraną przez klasę i zarejestrowaną w DependencyProperty systemie właściwości WPF.

Poniższy przykład rejestruje Tap zdarzenie kierowane, przechowuje zwrócone RoutedEvent wystąpienie i implementuje otokę zdarzeń CLR.

// Register a custom routed event using the Bubble routing strategy.
public static readonly RoutedEvent TapEvent = EventManager.RegisterRoutedEvent(
    name: "Tap",
    routingStrategy: RoutingStrategy.Bubble,
    handlerType: typeof(RoutedEventHandler),
    ownerType: typeof(CustomButton));

// Provide CLR accessors for adding and removing an event handler.
public event RoutedEventHandler Tap
{
    add { AddHandler(TapEvent, value); }
    remove { RemoveHandler(TapEvent, value); }
}
' Register a custom routed event using the Bubble routing strategy.
Public Shared ReadOnly TapEvent As RoutedEvent = EventManager.RegisterRoutedEvent(
    name:="Tap",
    routingStrategy:=RoutingStrategy.Bubble,
    handlerType:=GetType(RoutedEventHandler),
    ownerType:=GetType(CustomButton))

' Provide CLR accessors for adding and removing an event handler.
Public Custom Event Tap As RoutedEventHandler
    AddHandler(value As RoutedEventHandler)
        [AddHandler](TapEvent, value)
    End AddHandler

    RemoveHandler(value As RoutedEventHandler)
        [RemoveHandler](TapEvent, value)
    End RemoveHandler

    RaiseEvent(sender As Object, e As RoutedEventArgs)
        [RaiseEvent](e)
    End RaiseEvent
End Event

Strategie routingu

Zdarzenia trasowane używają jednej z trzech strategii routingu:

  • Bubbling: Początkowo wywoływane są programy obsługi zdarzeń w źródle zdarzeń. Następnie kierowane zdarzenie kieruje do kolejnych elementów nadrzędnych, wywołując ich programy obsługi zdarzeń z kolei, aż do momentu dotarcia do katalogu głównego drzewa elementów. Większość kierowanych zdarzeń używa strategii routingu bubbling. Zdarzenia kierowane przez bubbling są zwykle używane do zgłaszania zmian danych wejściowych lub stanu z kontrolek złożonych lub innych elementów interfejsu użytkownika.

  • Tunelowanie: początkowo wywoływane są programy obsługi zdarzeń w katalogu głównym drzewa elementów. Następnie kierowane zdarzenie kieruje do kolejnych elementów podrzędnych, wywołując ich programy obsługi zdarzeń z kolei, aż do momentu dotarcia do źródła zdarzeń. Zdarzenia, które podążają za trasą tunelowania, są również nazywane zdarzeniami w wersji zapoznawczej . Zdarzenia wejściowe WPF są zwykle implementowane jako pary w wersji zapoznawczej i bubbling.

  • Bezpośredni: wywoływane są tylko programy obsługi zdarzeń w źródle zdarzeń. Ta strategia niezwiązana z routingiem jest analogiczna do zdarzeń platformy interfejsu użytkownika Windows Forms, które są standardowymi zdarzeniami CLR. W przeciwieństwie do zdarzeń CLR zdarzenia kierowane bezpośrednio obsługują obsługę klas i mogą być używane przez klasy EventSetters i EventTriggers.

Dlaczego warto używać zdarzeń trasowanych?

Jako deweloper aplikacji nie zawsze musisz wiedzieć ani zadbać o to, aby zdarzenie, które obsługujesz, zostało zaimplementowane jako zdarzenie kierowane. Zdarzenia kierowane mają specjalne zachowanie, ale takie zachowanie jest w dużej mierze niewidoczne, jeśli obsługujesz zdarzenie w elemecie, który go wywołał. Jednak zdarzenia kierowane są istotne, gdy chcesz dołączyć program obsługi zdarzeń do elementu nadrzędnego w celu obsługi zdarzeń zgłaszanych przez elementy podrzędne, takich jak w kontrolce złożonej.

Odbiorniki zdarzeń kierowanych nie potrzebują kierowanych zdarzeń, które obsługują jako składowe swojej klasy. Dowolny UIElement lub ContentElement może być odbiornikiem zdarzeń dla dowolnego zdarzenia kierowanego. Ponieważ elementy wizualne pochodzą z UIElement klasy lub ContentElement, można użyć zdarzeń kierowanych jako koncepcyjnego "interfejsu", który obsługuje wymianę informacji o zdarzeniach między różnymi elementami w aplikacji. Koncepcja "interfejsu" dla zdarzeń kierowanych jest szczególnie stosowana do zdarzeń wejściowych.

Zdarzenia kierowane obsługują wymianę informacji o zdarzeniach między elementami wzdłuż trasy zdarzeń, ponieważ każdy odbiornik ma dostęp do tego samego wystąpienia danych zdarzenia. Jeśli jeden element zmieni coś w danych zdarzenia, ta zmiana jest widoczna dla kolejnych elementów w trasie zdarzeń.

Oprócz aspektu routingu można zaimplementować zdarzenie kierowane zamiast standardowego zdarzenia CLR z następujących powodów:

  • Niektóre funkcje stylów i tworzenia szablonów WPF, takie jak EventSetters i EventTriggers, wymagają, aby zdarzenie, do których się odwoływało, było zdarzeniem kierowanym.

  • Zdarzenia trasowane obsługują programy obsługi zdarzeń klasy , które obsługują zdarzenie przed wszystkimi procedurami obsługi wystąpień dla tego samego zdarzenia w dowolnym wystąpieniu klasy odbiornika. Ta funkcja jest przydatna w projektowaniu kontrolek, ponieważ program obsługi klas może wymuszać zachowania klas sterowane zdarzeniami, których nie można przypadkowo pominąć przez program obsługi wystąpień.

Dołączanie i implementowanie procedury obsługi zdarzeń kierowanych

W języku XAML program obsługi zdarzeń jest dołączany do elementu, deklarując nazwę zdarzenia jako atrybut elementu odbiornika zdarzeń. Wartość atrybutu to nazwa metody programu obsługi. Metoda obsługi musi zostać zaimplementowana w klasie częściowej za kodem dla strony XAML. Odbiornik zdarzeń jest elementem, w którym program obsługi zdarzeń jest dołączony i wywoływany.

W przypadku zdarzenia, które jest elementem członkowskim (dziedziczone lub w inny sposób) klasy odbiornika, można dołączyć program obsługi w następujący sposób:

<Button Name="Button1" Click="Button_Click">Click me</Button>

Jeśli zdarzenie nie jest członkiem klasy odbiornika, należy użyć kwalifikowanej nazwy zdarzenia w postaci <owner type>.<event name>. Na przykład, ponieważ StackPanel klasa nie implementuje Click zdarzenia, aby dołączyć program obsługi do StackPanelClick zdarzenia, które bąbelkuje do tego elementu, należy użyć składni kwalifikowanej nazwy zdarzenia:

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

Podpis metody obsługi zdarzeń w kodzie musi być zgodny z typem delegata dla zdarzenia kierowanego. Parametr sender delegata RoutedEventHandler dla Click zdarzenia określa element, do którego jest dołączony program obsługi zdarzeń. Parametr args delegata RoutedEventHandler zawiera dane zdarzenia. Zgodna implementacja kodu dla programu obsługi zdarzeń Button_Click może być:

private void Button_Click(object sender, RoutedEventArgs e)
{
    // Click event logic.
}
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    ' Click event logic.
End Sub

Chociaż RoutedEventHandler jest podstawowym delegatem procedury obsługi zdarzeń kierowanych, niektóre kontrolki lub scenariusze implementacji wymagają różnych delegatów, które obsługują bardziej wyspecjalizowane dane zdarzeń. Na przykład w przypadku zdarzenia kierowanego DragEnter program obsługi powinien zaimplementować delegata DragEventHandler . Dzięki temu kod programu obsługi może uzyskać dostęp do DragEventArgs.Data właściwości w danych zdarzeń, która zawiera ładunek schowka z operacji przeciągania.

Składnia XAML do dodawania procedur obsługi zdarzeń trasowanych jest taka sama jak w przypadku standardowych procedur obsługi zdarzeń CLR. Aby uzyskać więcej informacji na temat dodawania programów obsługi zdarzeń w języku XAML, zobacz XAML w WPF. Pełny przykład dołączania procedury obsługi zdarzeń do elementu przy użyciu języka XAML można znaleźć w temacie How to handle a routed event (Jak obsługiwać zdarzenie kierowane).

Aby dołączyć program obsługi zdarzeń dla zdarzenia kierowanego do elementu przy użyciu kodu, zazwyczaj dostępne są dwie opcje:

  • Bezpośrednio wywołaj metodę AddHandler . Programy obsługi zdarzeń trasowanych można zawsze dołączać w ten sposób. W tym przykładzie program obsługi zdarzeń jest dołączany Click do przycisku przy użyciu AddHandler metody :

    Button1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    Button1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    

    Aby dołączyć program obsługi zdarzenia przycisku Click do innego elementu w trasie zdarzenia, na przykład StackPanel o nazwie StackPanel1:

    StackPanel1.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Button_Click));
    
    StackPanel1.[AddHandler](ButtonBase.ClickEvent, New RoutedEventHandler(AddressOf Button_Click))
    
  • Jeśli kierowane zdarzenie implementuje otokę zdarzeń CLR, użyj składni zdarzeń specyficznych dla języka, aby dodać programy obsługi zdarzeń tak samo jak w przypadku standardowego zdarzenia CLR. Większość istniejących zdarzeń kierowanych WPF implementuje otokę CLR, włączając w ten sposób składnię zdarzeń specyficznych dla języka. Ten przykład dołącza procedurę Click obsługi zdarzeń do przycisku przy użyciu składni specyficznej dla języka:

    Button1.Click += Button_Click;
    
    AddHandler Button1.Click, AddressOf Button_Click
    

Aby zapoznać się z przykładem dołączania procedury obsługi zdarzeń w kodzie, zobacz How to add an event handler using code (Jak dodać procedurę obsługi zdarzeń przy użyciu kodu). Jeśli kodujesz w Visual Basic, możesz również użyć słowa kluczowego Handles , aby dodać programy obsługi w ramach deklaracji programu obsługi. Aby uzyskać więcej informacji, zobacz Visual Basic i obsługa zdarzeń WPF.

Koncepcja obsługi

Wszystkie zdarzenia kierowane mają wspólną klasę bazową dla danych zdarzeń, czyli klasę RoutedEventArgs . Klasa RoutedEventArgs definiuje właściwość logiczną Handled . Celem Handled właściwości jest umożliwianie programowi obsługi zdarzeń wzdłuż trasy zdarzeń oznaczenie zdarzenia kierowanego jako obsługiwanego. Aby oznaczyć zdarzenie jako obsługiwane, ustaw wartość Handled na true w kodzie procedury obsługi zdarzeń.

Wartość elementu Handled wpływa na sposób przetwarzania zdarzenia kierowanego podczas podróży wzdłuż trasy zdarzeń. Jeśli Handledtrue w udostępnionych danych zdarzeń zdarzenia kierowanego, programy obsługi dołączone do innych elementów dalej wzdłuż trasy zdarzeń zwykle nie będą wywoływane dla tego konkretnego wystąpienia zdarzenia. W przypadku najbardziej typowych scenariuszy obsługi oznaczanie zdarzenia jako obsługiwane skutecznie zatrzymuje kolejne programy obsługi wzdłuż trasy zdarzeń, niezależnie od tego, czy programy obsługi wystąpień lub klas, od reagowania na to konkretne wystąpienie zdarzenia. Jednak w rzadkich przypadkach, gdy potrzebujesz programu obsługi zdarzeń, aby reagować na kierowane zdarzenia, które zostały oznaczone jako obsługiwane, można:

Koncepcja Handled może mieć wpływ na sposób projektowania aplikacji i kodowania procedur obsługi zdarzeń. Można koncepcyjnie Handled utworzyć prosty protokół do przetwarzania kierowanych zdarzeń. Sposób użycia tego protokołu jest do Ciebie, ale oczekiwane użycie parametru Handled to:

  • Jeśli zdarzenie kierowane jest oznaczone jako obsługiwane, nie musi być ponownie obsługiwane przez inne elementy wzdłuż trasy.

  • Jeśli zdarzenie kierowane nie jest oznaczone jako obsługiwane, odbiorniki wcześniej w trasie zdarzeń nie mają procedury obsługi zdarzenia lub żaden z zarejestrowanych programów obsługi nie odpowiedział na zdarzenie w sposób, który uzasadnia oznaczenie zdarzenia jako obsługiwane. Programy obsługi w bieżącym odbiorniku mają trzy możliwe kursy akcji:

    • Nie podejmuj żadnych działań. Zdarzenie pozostaje nieobsługiwane i kieruje je do następnego odbiornika w drzewie.

    • Uruchom kod w odpowiedzi na zdarzenie, ale nie w pewnym stopniu, który uzasadnia oznaczenie zdarzenia jako obsługiwanego. Zdarzenie pozostaje nieobsługiwane i kieruje je do następnego odbiornika w drzewie.

    • Uruchom kod w odpowiedzi na zdarzenie w zakresie, który uzasadnia oznaczenie zdarzenia jako obsługiwanego. Oznacz zdarzenie jako obsługiwane w danych zdarzenia. Zdarzenie nadal kieruje do następnego odbiornika w drzewie, ale większość odbiorników nie będzie wywoływać dalszych procedur obsługi. Wyjątek to odbiorniki z procedurami obsługi, które zostały specjalnie zarejestrowane z ustawioną wartością handledEventsTootrue.

Aby uzyskać więcej informacji na temat obsługi zdarzeń kierowanych, zobacz Oznaczanie zdarzeń trasowanych jako obsługiwane i obsługa klas.

Mimo że deweloperzy, którzy obsługują tylko rozsyłane zdarzenie na obiekcie, który go podniósł, mogą nie być zaniepokojeni innymi odbiornikami, dobrym rozwiązaniem jest oznaczenie zdarzenia jako obsłużonego mimo to. Uniemożliwia to nieprzewidziane skutki uboczne, jeśli element dalej wzdłuż trasy zdarzeń ma procedurę obsługi dla tego samego zdarzenia kierowanego.

Programy obsługi klas

Programy obsługi zdarzeń trasowanych mogą być procedurami obsługi wystąpień lub procedurami obsługi klas . Procedury obsługi klas dla danej klasy są wywoływane przed każdym procedurą obsługi wystąpień odpowiadającą na to samo zdarzenie w dowolnym wystąpieniu tej klasy. Ze względu na to zachowanie, gdy zdarzenia kierowane są oznaczone jako obsługiwane, są one często oznaczone jako takie w programach obsługi klas. Istnieją dwa typy programów obsługi klas:

Niektóre kontrolki WPF mają nieodłączną obsługę klas dla niektórych zdarzeń trasowanych. Obsługa klas może dać wygląd na zewnątrz, że zdarzenie kierowane nie jest nigdy zgłaszane, ale w rzeczywistości jest oznaczone jako obsługiwane przez program obsługi klas. Jeśli potrzebujesz programu obsługi zdarzeń, aby odpowiedzieć na obsługiwane zdarzenie, możesz zarejestrować program obsługi z ustawioną wartością handledEventsTootrue. Aby uzyskać więcej informacji na temat implementowania własnych procedur obsługi klas lub pracy wokół niepożądanej obsługi klas, zobacz Oznaczanie kierowanych zdarzeń jako obsłużonych i obsługa klas.

Zdarzenia dołączone w WPF

Język XAML definiuje również specjalny typ zdarzenia nazywany dołączonym zdarzeniem. Dołączone zdarzenia mogą służyć do definiowania nowego zdarzenia kierowanego w klasie innej niż element i zgłaszać to zdarzenie na dowolnym elemenie w drzewie. W tym celu należy zarejestrować dołączone zdarzenie jako zdarzenie kierowane i podać określony kod zapasowy , który obsługuje dołączone funkcje zdarzeń. Ponieważ dołączone zdarzenia są rejestrowane jako zdarzenia kierowane, po wywołaniu elementu są propagowane przez drzewo elementów.

W składni XAML dołączone zdarzenie jest określane przez jego nazwę zdarzenia i typ właściciela w postaci <owner type>.<event name>. Ponieważ nazwa zdarzenia jest kwalifikowana przy użyciu nazwy jego typu właściciela, składnia umożliwia dołączanie zdarzenia do dowolnego elementu, który można utworzyć wystąpienie. Ta składnia ma również zastosowanie do procedur obsługi zwykłych zdarzeń kierowanych, które dołączają do dowolnego elementu wzdłuż trasy zdarzeń. Można również dołączać programy obsługi dla dołączonych zdarzeń w kodzie, wywołując metodę AddHandler w obiekcie, do którego powinien zostać dołączony program obsługi.

System wejściowy WPF intensywnie używa dołączonych zdarzeń. Jednak prawie wszystkie dołączone zdarzenia są udostępniane jako równoważne nieprzyłączane zdarzenia kierowane przez elementy podstawowe. Rzadko będziesz używać dołączonych zdarzeń lub obsługiwać je bezpośrednio. Na przykład łatwiej jest obsłużyć bazowe dołączone Mouse.MouseDown zdarzenie na UIElement obiekcie za pośrednictwem równoważnego UIElement.MouseDown zdarzenia kierowanego niż za pomocą dołączonej składni zdarzeń w języku XAML lub kod-behind.

Aby uzyskać więcej informacji na temat dołączonych zdarzeń w WPF, zobacz Omówienie dołączonych zdarzeń.

Kwalifikowane nazwy zdarzeń w języku XAML

Składnia <owner type>.<event name> kwalifikuje nazwę zdarzenia z nazwą jego typu właściciela. Ta składnia umożliwia dołączanie zdarzenia do dowolnego elementu, a nie tylko elementów, które implementują zdarzenie jako element członkowski ich klasy. Składnia ma zastosowanie podczas dołączania programów obsługi w języku XAML dla dołączonych zdarzeń lub kierowanych zdarzeń na dowolnych elementach wzdłuż trasy zdarzeń. Rozważmy scenariusz, w którym chcesz dołączyć procedurę obsługi do elementu nadrzędnego w celu obsługi zdarzeń kierowanych zgłoszonych w elementach podrzędnych. Jeśli element nadrzędny nie ma zdarzenia kierowanego jako element członkowski, należy użyć składni kwalifikowanej nazwy zdarzenia. Na przykład:

<StackPanel Name="StackPanel1" Button.Click="Button_Click">
    <Button>Click me</Button>
</StackPanel>

W tym przykładzie odbiornik elementu nadrzędnego, do którego dodano procedurę StackPanelobsługi zdarzeń, to . Jednak Click zdarzenie kierowane jest implementowane i wywoływane w ButtonBase klasie i dostępne dla Button klasy poprzez dziedziczenie. Button Mimo że klasa "jest właścicielem" Click zdarzenia, kierowany system zdarzeń zezwala na programy obsługi dla dowolnego kierowanego zdarzenia do dołączenia do dowolnego UIElement odbiornika lub ContentElement wystąpienia, które w przeciwnym razie mogą mieć programy obsługi dla zdarzenia CLR. Domyślną przestrzenią nazw tych kwalifikowanych nazw atrybutów zdarzeń jest zazwyczaj domyślna xmlns przestrzeń nazw WPF xmlns , ale można również określić prefiksowane przestrzenie nazw dla niestandardowych zdarzeń trasowanych. Aby uzyskać więcej informacji na temat xmlnsprogramu , zobacz Przestrzenie nazw XAML i mapowanie przestrzeni nazw dla języka WPF XAML.

Zdarzenia wejściowe WPF

Jednym z częstych zastosowań kierowanych zdarzeń na platformie WPF są zdarzenia wejściowe. Zgodnie z konwencją zdarzenia kierowane przez platformę WPF, które są zgodne z trasą tunelowania, mają nazwę poprzedzoną prefiksem "Wersja zapoznawcza". Prefiks podglądu oznacza, że zdarzenie podglądu zostanie zakończone przed rozpoczęciem sparowanego zdarzenia bubbling. Zdarzenia wejściowe często występują w parach, z jednym zdarzeniem podglądu, a drugim zdarzeniem kierowanym przez bubbling. Na przykład PreviewKeyDown i KeyDown. Pary zdarzeń współużytkuje to samo wystąpienie danych zdarzenia, które dla PreviewKeyDown i KeyDown mają typ KeyEventArgs. Czasami zdarzenia wejściowe mają tylko wersję bubbling lub tylko wersję kierowaną bezpośrednio. W dokumentacji interfejsu API przekierowane tematy zdarzeń odwołujące się do wielu par zdarzeń i wyjaśnienie strategii routingu dla każdego kierowanego zdarzenia.

Zdarzenia wejściowe WPF, które występują w parach, są implementowane tak, aby pojedyncza akcja użytkownika z urządzenia wejściowego, taka jak naciśnięcie przycisku myszy, podnosi podgląd i bubbling kierowane zdarzenia w sekwencji. Najpierw zdarzenie w wersji zapoznawczej jest wywoływane i kończy swoją trasę. Po zakończeniu zdarzenia w wersji zapoznawczej zdarzenie bubbling jest wywoływane i kończy swoją trasę. Wywołanie RaiseEvent metody w klasie implementowania, która zgłasza zdarzenie bubbling ponownie używa danych zdarzenia ze zdarzenia podglądu dla zdarzenia bubbling.

Zdarzenie wejściowe podglądu oznaczone jako obsługiwane nie będzie wywoływać żadnych normalnie zarejestrowanych programów obsługi zdarzeń dla pozostałej części trasy podglądu, a sparowane zdarzenie bubbling nie zostanie podniesione. To zachowanie obsługi jest przydatne w przypadku projektantów kontrolek złożonych, którzy chcą, aby zdarzenia wejściowe oparte na teście trafień lub zdarzenia wejściowe oparte na fokusie były zgłaszane na najwyższym poziomie kontroli. Elementy najwyższego poziomu kontrolki mają możliwość obsługi zdarzeń podglądu klasy z podskładników sterowania, aby "zastąpić" je zdarzeniem specyficznym dla kontroli najwyższego poziomu.

Aby zilustrować działanie przetwarzania zdarzeń wejściowych, rozważmy następujący przykład zdarzenia wejściowego. Na poniższej ilustracji leaf element #2 drzewa jest źródłem zdarzeń i PreviewMouseDownMouseDown sparowanych:

Event routing diagram.

Kolejność przetwarzania zdarzeń po akcji myszy w dół elementu liścia #2 to:

  1. PreviewMouseDown zdarzenie tunelowania w elemedycie głównym.
  2. PreviewMouseDown zdarzenie tunelowania dla elementu pośredniego #1.
  3. PreviewMouseDown zdarzenie tunelowania elementu liścia #2, które jest elementem źródłowym.
  4. MouseDown Zdarzenie bubbling elementu liścia #2, który jest elementem źródłowym.
  5. MouseDown Bubbling zdarzenie na element pośredni #1.
  6. MouseDown Bubbling zdarzenie na elemecie głównym.

Delegat programu obsługi zdarzeń kierowanych zawiera odwołania zarówno do obiektu, który wywołał zdarzenie, jak i obiekt, w którym wywoływano procedurę obsługi. Obiekt, który pierwotnie zgłosił zdarzenie, jest zgłaszany przez Source właściwość w danych zdarzenia. Obiekt, w którym wywoływano procedurę obsługi, jest zgłaszany przez parametr nadawcy . W przypadku dowolnego wystąpienia zdarzenia kierowanego obiekt, który zgłosił zdarzenie, nie zmienia się, ponieważ zdarzenie przechodzi przez drzewo elementów, ale sender robi. W krokach 3 i 4 z poprzedniego diagramu Source obiekt i sender są tym samym obiektem.

Jeśli program obsługi zdarzeń wejściowych ukończy logikę specyficzną dla aplikacji wymaganą do rozwiązania zdarzenia, należy oznaczyć zdarzenie wejściowe jako obsługiwane. Zazwyczaj po oznaczeniu Handledzdarzenia wejściowego programy obsługi dalej wzdłuż trasy zdarzenia nie są wywoływane. Jednak programy obsługi zdarzeń wejściowych zarejestrowane za pomocą zestawu true parametrów handledEventsToo będą wywoływane nawet wtedy, gdy zdarzenie jest oznaczone jako obsługiwane. Aby uzyskać więcej informacji, zobacz Zdarzenia w wersji zapoznawczej i Oznaczanie zdarzeń kierowanych jako obsługiwane i obsługa klas.

Koncepcja par zdarzeń w wersji zapoznawczej i bubbling, z udostępnionymi danymi zdarzeń i sekwencyjnym podnoszeniem zdarzenia w wersji zapoznawczej, a następnie zdarzenie bubbling dotyczy tylko niektórych zdarzeń wejściowych WPF, a nie do wszystkich zdarzeń kierowanych. Jeśli zaimplementujesz własne zdarzenie wejściowe w celu rozwiązania zaawansowanego scenariusza, rozważ zastosowanie metody pary zdarzeń wejściowych WPF.

Jeśli implementujesz własną kontrolkę złożoną, która reaguje na zdarzenia wejściowe, rozważ użycie zdarzeń w wersji zapoznawczej w celu pomijania i zastępowania zdarzeń wejściowych zgłoszonych w podkomponentach zdarzeniem najwyższego poziomu, które reprezentuje pełną kontrolę. Aby uzyskać więcej informacji, zobacz Oznaczanie zdarzeń trasowanych jako obsługiwane i obsługa klas.

Aby uzyskać więcej informacji na temat systemu wejściowego WPF oraz sposobu interakcji danych wejściowych i zdarzeń w typowych scenariuszach aplikacji, zobacz Omówienie danych wejściowych.

EventSetters i EventTriggers

W stylach znaczników można uwzględnić wstępnie zadeklarowaną składnię obsługi zdarzeń XAML przy użyciu klasy EventSetter. Po przetworzeniu kodu XAML program obsługi przywoływany jest dodawany do wystąpienia stylizowanego. Można zadeklarować tylko zdarzenie EventSetter kierowane. W poniższym przykładzie metoda obsługi zdarzeń, do których odwołuje ApplyButtonStyle się odwołanie, jest implementowana w kodzie za pomocą kodu.

<StackPanel>
    <StackPanel.Resources>
        <Style TargetType="{x:Type Button}">
            <EventSetter Event="Click" Handler="ApplyButtonStyle"/>
        </Style>
    </StackPanel.Resources>
    <Button>Click me</Button>
    <Button Click="Button_Click">Click me</Button>
</StackPanel>

Style Prawdopodobnie węzeł zawiera już inne informacje o stylu, które odnoszą się do kontrolek określonego typu, i że EventSetter element ten jest częścią tych stylów, promuje ponowne użycie kodu nawet na poziomie znaczników. EventSetter Ponadto nazwy metod abstrakcyjnych dla procedur obsługi z dala od ogólnej aplikacji i znaczników stron.

Inna wyspecjalizowana składnia łącząca zdarzenia kierowane i funkcje animacji WPF to EventTrigger. Podobnie jak w przypadku elementu EventSetter, można zadeklarować tylko dla EventTrigger zdarzenia kierowanego. EventTrigger Zazwyczaj element jest zadeklarowany jako część stylu, ale EventTrigger może być zadeklarowany na elementach na poziomie strony w ramach Triggers kolekcji lub w obiekcie ControlTemplate. Element EventTrigger umożliwia określenie Storyboard , które jest uruchamiane za każdym razem, gdy zdarzenie kierowane osiągnie element w swojej trasie, który deklaruje zdarzenie EventTrigger dla tego zdarzenia. Zaletą EventTrigger nieco obsługi zdarzenia i spowodowania uruchomienia istniejącej scenorysu jest to, że zapewnia lepszą EventTrigger kontrolę nad scenorysem i jego zachowaniem w czasie wykonywania. Aby uzyskać więcej informacji, zobacz Używanie wyzwalaczy zdarzeń do kontrolowania scenorysu po jego uruchomieniu.

Więcej informacji o zdarzeniach kierowanych

W tym artykule możesz użyć pojęć i wskazówek jako punktu wyjścia podczas tworzenia niestandardowych zdarzeń kierowanych we własnych klasach. Zdarzenia niestandardowe można również obsługiwać za pomocą wyspecjalizowanych klas danych zdarzeń i delegatów. Właściciel zdarzenia kierowanego może być dowolną klasą, ale zdarzenia kierowane muszą być wywoływane przez klasy i obsługiwane przez UIElement lub ContentElement pochodne klasy, aby być przydatne. Aby uzyskać więcej informacji na temat zdarzeń niestandardowych, zobacz Tworzenie niestandardowego zdarzenia kierowanego.

Zobacz też