czwartek, 16 sierpnia 2012

[WPF] Routed Events

Routed events to specjalny rodzaj zdarzeń, które świetnie nadają się do pracy z hierarchiczną strukturą kontrolek. Kiedy Routed Event jest wywoływany, może być propagowany w górę lub dół drzewa, co może się okazać w wielu sytuacjach bardzo użytecznym rozwiązaniem. Dzięki nim logika aplikacji nie musi pamiętać o strukturze drzewa wizualnego, co jest bardzo dobrą właściwością np. przy stylowaniu. Istnieje pewna analogia pomiędzy RE, a Dependency Property. Oba  są definiowane jako publiczne statyczne pole, rejestrowane w podobny sposób i oba można "opakować". Dla DP pobiera się lub ustawia wartość, dla RE dodaje lub zabiera Event Handler. Istnieją trzy strategie routingu :
  • Tunneling - zdarzenie jest wywoływane najpierw w korzeniu drzewa, a następnie na niższych poziomach aż do źródła wywołania lub obsługi zdarzenia
  • Bubbling - zdarzenie wołane u źródła przechodzi na wyższe poziomy do korzenia lub do miejsca, w którym zostaje obsłużone
  • Direct - zachowanie takie, jak u zwykłych .NETowych eventów (wołane tylko w źródle), ale można tworzyć dla tego zdarzenia event triggery.
 Obsługa RE przypomina obsługę zwykłych zdarzeń (metody z pierwszym parametrem typu object (sender), oraz drugim typu eventArgs lub pochodnym).

Klasa UI element oferuje wiele eventów ze zdefiniowaną strategią bubbling jak i tunneling. Przez konwencję, zdarzenia ze strategią tunneling nazywane są z przedrostkiem Preview. Np. PreviewMouseDown to zdarzenie ze strategią tunneling, podczas gdy MouseDown ma zdefiniowaną strategię typu bubbling.

Na poniższym przykładzie można zaobserwować kolejność, w jakiej są wywoływane zdarzenia, oraz warunki, w jakich routing jest przerwany.

<Window x:Class="Routed_Events.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid MouseLeftButtonDown="Grid_MouseLeftButtonDown"           
          PreviewMouseRightButtonDown="Grid_PreviewMouseRightButtonDown">
            <Button Content="Click Me"            
            PreviewMouseLeftButtonDown="Button_PreviewMouseLeftButtonDown" 
            MouseRightButtonDown="Button_MouseRightButtonDown" Margin="75,112,278,140" />

        </Grid>
       
    </Grid>
   
    
</Window>


  public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Debug.WriteLine("LeftMouseButton handled by grid1");
        }

        private void Grid_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            Debug.WriteLine("RightMouseButton handled by grid1");
        }

        private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Debug.WriteLine("LeftMouseButton handled by button1");
        }

        private void Button_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            Debug.WriteLine("RightMousebutton handled by button1");
        }
     
    }


LeftMouseButton handled by button1
RightMouseButton handled by grid1
RightMousebutton handled by button1

Programista może definiować swoje Routed Events w następujący sposób:

public class MyControl : Button
    {
        public static readonly RoutedEvent EvenClickEvent;
        public static int Counter = 0;

        static MyControl()
        {

            MyControl.EvenClickEvent = EventManager.RegisterRoutedEvent("EvenClick",
                RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(MyControl));
        }

        public event RoutedEventHandler EvenClick
        {
            add { AddHandler(MyControl.EvenClickEvent, value); }
            remove { RemoveHandler(MyControl.EvenClickEvent, value); }
        }

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            if(Counter++ % 2 != 0)
                RaiseEvent(new RoutedEventArgs(MyControl.EvenClickEvent, this));
        }
    }

Dzięki temu można obsługiwać taki event z poziomu xaml.

Brak komentarzy:

Prześlij komentarz