Witajcie dziś kolejna porcja przygotowania do egzaminu WPF (70-502). Zgodnie z obietnicą dziś będzie trochę o eventach.

Tak więc w WPF eventy możemy deklarować:
w XAMLu:

<Grid Loaded=”Grid_Loaded” x:Name=”grid”>

oraz ‘standardowo’ w kodzie:

grid.Loaded += grid_Loaded;

I jeśli chodzi o konfigurację eventów to byłoby chyba na tyle, gdyby nie fakt, że WPF troszkę w tym fragmencie burzy ‘nasz porządek wszechświata’.

Routed Events

Czym są ‘rutowalne’ eventy? (pisałem już zresztą o nich kiedyś) Są to eventy, które potrafią trawersować logiczne i wizualne drzewo elementów (czym są tę drzewa pisałem już dawno tu). Dla przykładu klikając na przycisk, który jako “treść” ma jakiś wzór. Pomimo tego, że tak naprawdę klikamy na wzór to przycisk widzi kliknięcie, ponieważ event przechodzi po drzewie (czy to logicznym czy wizualnym). Jak dodać taki ‘rutowalny’ event. Nie jest to bardzo skomplikowane.

  • Zaczynamy od definicji eventu:

    public static readonly RoutedEvent NewRoutedEvent;

    Z ciekawostek warto zauważyć, że jest to statyczna deklaracja o typie RoutedEvent.

  • Kolejnym krokiem jest inicjalizacja tegoż:

    Window1.NewRoutedEvent =

                EventManager.RegisterRoutedEvent(“New”, RoutingStrategy.Bubble,

                                        typeof(RoutedEventHandler),

                                        typeof(Window1));

    i dokonujemy tego w statycznym konstruktorze. Aby zarejestrować nowy event musimy nadać mu unikalną nazwę (w obrębie danego typu), wybrać strategię rutowania (więcej za chwilę), oraz określić jakiego typu jest to event, oraz kto jest ‘odpowiedzialny’ za niego.

  • A zaraz po tym dodajemy (opcjonalne) przypinanie i odpinanie handlerów (kodu obsługującego??):

    public event  RoutedEventHandler NewRouted

    {

        add { AddHandler(Window1.NewRoutedEvent, value); }

        remove { AddHandler(Window1.NewRoutedEvent, value); }

    }

  • Wywołanie to proste:

    RaiseEvent(new RoutedEventArgs(NewRoutedEvent, this));

Strategie rutowania

Jednym z parametrów przy rejestracji rodzaj strategii, która będzie użyta do propagacji eventu. Możliwe opcje to:

public enum RoutingStrategy

{

    Bubble,

    Direct,

    Tunnel

}

  • Bubble – event jest w pierwszej kolejności wywoływany na źródłowym elemencie i porusza się ‘w górę’ drzewa, aż do elementu głównego (lub do czasu jego obsłużenia)
  • Direct – event wywoływany jest tylko i wyłącznie źródłowym eventu. Zachowanie identyczne do standardowych .NET’owych eventów
  • Tunnel – event na samym początku wywoływany jest na elemencie głównym i porusza się ‘w dół’, aż do elementu źródłowego.

Idea za tym, aby mieć event ‘podróżujący’ w dół oraz w górę jest taka, że eventy podróżujące w dół (zgodnie z konwencją ze słówkiem Preview) są wywoływane jako pierwsze i dzięki temu mamy możliwość zablokowania (poprzez ustawienie Handled na true) takiego eventa przed dotarciem do elementu źródłowego i przed propagacją w górę.

Przez to, że eventy te są trochę bardziej skomplikowane od zwykłych klasa RoutedEventArgs została rozbudowana o kilka pól:

  • Source – element w drzewie logicznym, który zapoczątkował event
  • OrginalSource – element w drzewie wizualnym, który zapoczątkował event
  • Handled – jeśli ustawione na true zapobiega dalszej propagacji eventu
  • RoutedEvent – zawiera konkretną instancję rutowalnego eventu

Attached events

Można się zastanawiać jak taki event może sobie podróżować po drzewie kontrolek w przypadku, gdy dana kontrolka wspiera dany typ eventu. Ale co w przypadku, gdy tego nie robi?

Z pomocą przychodzą nam Attached events. Przypinanie do takiego eventu działa na podobnej zasadzie jak Attached properties. Jedyne co musimy zrobić to poprzedzić nazwę eventu, nazwą kontrolki, która go udostępnia np.:

<Grid ButtonBase.Click=”grid_Click”>

i to wszystko. Od tej chwili grid będzie dostawał powiadomienia, gdy przycisk zostanie naciśnięty.

Na dziś tyle o eventach – następnym razem: Commands.