Przegląd zdarzeń trasowanych (WPF .NET)

Deweloperzy aplikacji i autorzy składników programu Windows Presentation Foundation (WPF) 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 i oferuje wskazówki dotyczące tego, kiedy i jak reagować na kierowane zdarzenia.

Ważne

Dokumentacja przewodnika dla komputerów dla platform .NET 7 i .NET 6 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 oraz 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 Extensible Application Markup Language (XAML) i wiedzieć, jak pisać aplikacje WPF.

Co to jest zdarzenie kierowane?

Możesz rozważyć kierowanie zdarzeń z perspektywy funkcjonalności 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łem zdarzenia jest element lub obiekt, 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, tak 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 w 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 częściowe drzewo elementów:

<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 sposób pokazany:

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 zgłaszane Click jest zdarzenie, które jest bąbelkowe od przycisku do elementu głównego. Button Elementy i Border nie mają dołączonych procedur obsługi zdarzeń, ale nieStackPanel. 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ń. Gdy Click zdarzenie dociera do StackPanel elementu, 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 kierowane, jest identyfikowany jako RoutedEventArgs.Source 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 kontrolna i hermetyzacja: Różne kontrolki w WPF mają bogaty con tryb namiotu l. Można na przykład 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, co 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 zdarzenia 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 wyświetlonym 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 RoutedEvent identyfikator pola, aby zapewnić niezawodną technikę identyfikacji zdarzeń, która nie wymaga odbicia statycznego lub czasu wykonywania w celu zidentyfikowania zdarzenia.

Jak implementowane są 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 sposobu, w jaki właściwość zależności jest właściwością CLR, która jest wspierana przez DependencyProperty klasę i zarejestrowana w 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 kierowane używają jednej z trzech strategii routingu:

  • Bubbling: Początkowo są wywoływane programy obsługi zdarzeń w źródle zdarzeń. Zdarzenie kierowane następnie kieruje do kolejnych elementów nadrzędnych, wywołując ich programy obsługi zdarzeń z kolei, aż dotrze do katalogu głównego drzewa elementów. Większość zdarzeń kierowanych 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. Zdarzenie kierowane następnie kieruje do kolejnych elementów podrzędnych, wywołując ich programy obsługi zdarzeń z kolei do momentu dotarcia do źródła zdarzeń. Zdarzenia, które są zgodne z trasą tunelowania, są również nazywane zdarzeniami w wersji zapoznawczej . Zdarzenia wejściowe WPF są zwykle implementowane jako pary podglądu i bubbling.

  • Bezpośredni: wywoływane są tylko programy obsługi zdarzeń w źródle zdarzeń. Ta strategia niezwiązana ze routingiem jest analogiczna do zdarzeń struktury interfejsu użytkownika systemu 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 podniósł. 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, na przykład w kontrolce złożonej.

Odbiorniki zdarzeń kierowanych nie potrzebują kierowanych zdarzeń, które obsługują, aby być członkami swojej klasy. Dowolny UIElement lub ContentElement może być odbiornikiem zdarzeń dla dowolnego zdarzenia kierowanego. Ponieważ elementy wizualne pochodzą z UIElement elementu lub ContentElement, można użyć zdarzeń trasowanych jako koncepcyjnego "interfejsu", który obsługuje wymianę informacji o zdarzeniach między różnymi elementami w aplikacji. Koncepcja "interfejsu" dla zdarzeń kierowanych ma szczególnie zastosowanie 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 będzie widoczna dla kolejnych elementów trasy zdarzeń.

Oprócz aspektu routingu możesz wybrać zaimplementowanie zdarzenia kierowanego 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 przywoływało zdarzenie jako zdarzenie kierowane.

  • 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 być 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ć kwalifikowanej składni nazwy zdarzenia:

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

Podpis metody obsługi zdarzeń w kodzie za pomocą kodu musi być zgodny z typem delegata dla zdarzenia kierowanego. Parametr sender delegata RoutedEventHandler zdarzenia Click określa element, do którego jest dołączony program obsługi zdarzeń. Parametr args delegata RoutedEventHandler zawiera dane zdarzenia. Zgodna implementacja kodu za Button_Click pomocą programu obsługi zdarzeń 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 pełnomocnikiem 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 sposobu dołączania programu 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 Click program obsługi zdarzeń jest dołączany 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ć procedurę 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 zdarzenie kierowane 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. W tym przykładzie program obsługi zdarzeń jest dołączany Click 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 języku 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 Obsługa zdarzeń Visual Basic i WPF.

Koncepcja obsługi

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

Wartość Handled ma wpływ na sposób przetwarzania zdarzenia kierowanego podczas podróży wzdłuż trasy zdarzeń. Jeśli Handled znajduje się true w udostępnionych danych zdarzenia kierowanego, programy obsługi dołączone do innych elementów dalej wzdłuż trasy zdarzenia zwykle nie będą wywoływane dla tego konkretnego wystąpienia zdarzenia. W przypadku najbardziej typowych scenariuszy obsługi oznaczanie zdarzenia jako obsłużonego skutecznie zatrzymuje kolejne programy obsługi wzdłuż trasy zdarzeń, niezależnie od tego, czy programy obsługi wystąpień, czy klas, od reagowania na to konkretne wystąpienie zdarzenia. Jednak w rzadkich przypadkach, w których 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 programów obsługi zdarzeń. Koncepcyjnie można określić Handled jako prosty protokół do przetwarzania zdarzeń kierowanych. 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 trasy.

  • Jeśli zdarzenie kierowane nie jest oznaczone jako obsługiwane, odbiorniki wcześniej w trasie zdarzenia 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:

    • W ogóle nie podejmij żadnych działań. Zdarzenie pozostaje nieobsługiwane i kieruje je do następnego odbiornika w drzewie.

    • Uruchom kod w odpowiedzi na zdarzenie, ale nie do pewnego stopnia, 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ługiwane. Oznacz zdarzenie jako obsługiwane w danych zdarzenia. Zdarzenie nadal kieruje do następnego odbiornika w drzewie, ale większość odbiorników nie wywołuje dalszych procedur obsługi. Wyjątek dotyczy odbiorników 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ń trasowanych, zobacz Oznaczanie zdarzeń trasowanych zgodnie z obsługą 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ługiwane mimo to. Zapobiega to nieoczekiwanym skutkom ubocznym, 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 . Programy 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 procedur obsługi klas:

  • Programy obsługi zdarzeń klasy statycznej, które są rejestrowane przez wywołanie RegisterClassHandler metody w konstruktorze klasy statycznej.
  • Zastąpić programy obsługi zdarzeń klasy, które są rejestrowane przez zastępowanie metod zdarzeń wirtualnych klasy bazowej. Metody zdarzeń wirtualnych klasy bazowej istnieją głównie dla zdarzeń wejściowych i mają nazwy rozpoczynające się od w polu Nazwa> zdarzenia i Nazwa> zdarzenia OnPreview<.<

Niektóre kontrolki WPF mają nieodłączną obsługę klas dla niektórych zdarzeń trasowanych. Obsługa klas może sprawiać wrażenie na zewnątrz, że zdarzenie kierowane nigdy nie jest wywoływane, ale w rzeczywistości jest oznaczane jako obsługiwane przez procedurę obsługi klas. Jeśli potrzebujesz programu obsługi zdarzeń, aby odpowiedzieć na obsługiwane zdarzenie, możesz zarejestrować procedurę 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 zdarzeń trasowanych zgodnie z obsługą i obsługa klas.

Dołączone zdarzenia 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ć. Ta składnia ma również zastosowanie do procedur obsługi regularnych zdarzeń kierowanych, które dołączają do dowolnego elementu wzdłuż trasy zdarzeń. Można również dołączyć 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łączone 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 za UIElement pośrednictwem równoważnego UIElement.MouseDown zdarzenia kierowanego niż przy użyciu 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 swojej 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ć program obsługi do elementu nadrzędnego w celu obsługi zdarzeń kierowanych w elementach podrzędnych. Jeśli element nadrzędny nie ma zdarzenia kierowanego jako element członkowski, musisz użyć kwalifikowanej składni nazwy zdarzenia. 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 jest dodawany program obsługi zdarzeń, jest elementem StackPanel. Jednak Click zdarzenie kierowane jest implementowane i wywoływane w ButtonBase klasie i dostępne dla klasy za pośrednictwem dziedziczenia Button . Button Mimo że klasa "jest właścicielem" Click zdarzenia, kierowany system zdarzeń zezwala na programy obsługi dla dowolnego zdarzenia kierowanego 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

Jedną z częstych aplikacji kierowanych zdarzeń na platformie WPF są zdarzenia wejściowe. Zgodnie z konwencją zdarzenia kierowane przez WPF, które są zgodne z trasą tunelowania, mają nazwę prefiksu "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 w wersji zapoznawczej i drugim zdarzeniem kierowanym przez bubbling. Przykład: PreviewKeyDown i KeyDown. Pary zdarzeń współużytkuje to samo wystąpienie danych zdarzenia, dla którego typ PreviewKeyDown i KeyDown jest typu KeyEventArgs. Czasami zdarzenia wejściowe mają tylko wersję bubbling lub tylko wersję kierowaną bezpośrednio. W dokumentacji interfejsu API rozsyłane tematy zdarzeń, które odwołują się do trasowanych par zdarzeń i wyjaśnić strategię 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, spowoduje podniesienie podglądu i rozsyłanie kierowanych zdarzeń w sekwencji. Najpierw jest wywoływane zdarzenie w wersji zapoznawczej i kończy swoją trasę. Po zakończeniu zdarzenia w wersji zapoznawczej jest wywoływane zdarzenie bubbling i kończy swoją trasę. Wywołanie RaiseEvent metody w klasie implementowania, która wywołuje 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. Takie zachowanie obsługi jest przydatne w przypadku projektantów kontrolek złożonych, którzy chcą zgłaszać zdarzenia wejściowe oparte na testach trafień lub zdarzenia wejściowe oparte na fokusie 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ć sposób działania 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 w elemenie liścia #2, który jest elementem źródłowym.
  4. MouseDown Zdarzenie bubbling elementu liścia #2, który jest elementem źródłowym.
  5. MouseDown Zdarzenie bubbling w elemecie pośrednim #1.
  6. MouseDown Zdarzenie bubbling na elemercie głównym.

Delegat procedury obsługi zdarzeń kierowanych zawiera odwołania zarówno do obiektu, który zgłosił zdarzenie, jak i obiektu, 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 nie. W krokach 3 i 4 powyższego diagramu Source obiekty 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 handledEventsToo pomocą zestawu true parametrów będą wywoływane nawet wtedy, gdy zdarzenie jest oznaczone jako obsługiwane. Aby uzyskać więcej informacji, zobacz Podgląd zdarzeń i Oznaczanie kierowanych zdarzeń 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 zdarzeniem bubbling, ma zastosowanie tylko do niektórych zdarzeń wejściowych WPF, a nie do wszystkich kierowanych zdarzeń. Jeśli zaimplementujesz własne zdarzenie wejściowe w celu rozwiązania zaawansowanego scenariusza, rozważ zastosowanie podejścia pary zdarzeń wejściowych WPF.

Jeśli wdrażasz własną złożoną kontrolkę, która reaguje na zdarzenia wejściowe, rozważ użycie zdarzeń podglądu w celu pomijania i zastępowania zdarzeń wejściowych zgłaszanych w podskładników 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 elementu EventSetter. Po przetworzeniu kodu XAML program obsługi przywoływany jest dodawany do wystąpienia stylizowanego. Można zadeklarować EventSetter tylko dla zdarzenia kierowanego. 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>

Prawdopodobnie Style 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 abstrakcji dla procedur obsługi z dala od ogólnej aplikacji i znaczników strony.

Inną wyspecjalizowaną składnią, która łączy trasy zdarzenia i funkcje animacji WPF jest .EventTrigger Podobnie jak w przypadku EventSetterelementu , można zadeklarować EventTrigger tylko dla zdarzenia kierowanego. Zazwyczaj element EventTrigger jest zadeklarowany jako część stylu, ale EventTrigger można go zadeklarować 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 jego trasie, który deklaruje EventTrigger dla tego zdarzenia. Zaletą EventTrigger ponad just obsługi zdarzenia i spowodowania uruchomienia istniejącej scenorysu jest to, że zapewnia lepszą kontrolę nad scenorysem EventTrigger 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 klasy lub ContentElement pochodne, aby były przydatne. Aby uzyskać więcej informacji na temat zdarzeń niestandardowych, zobacz Tworzenie niestandardowego zdarzenia kierowanego.

Zobacz też