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.