WPF .NET Core - Routed Events
Written by Mike James   
Thursday, 22 October 2020
Article Index
WPF .NET Core - Routed Events
Adding and removing handlers
Types of Routed Event
Creating your own routed events
Routed events – good or bad

Types of Routed Event

So far we have only considered routed events that “bubble” up the element hierarchy but there are three distinct types of routed event:

  • Direct
    This provides the standard CLR event behaviour in that only the source of the event invokes handlers. However a direct routed event is still a routed event and can be used in with other routed event specific facilities.
  • Bubbling
    This is the behaviour we have been considering so far. The event “bubbles” up the element hierarchy.
  • Tunnelling
    A tunnelling event is the strangest of all. It always starts at the top of the element hierarchy, irrespective of its source, and then  “bubbles down” to the event’s source element.

The type of routing an event uses is all a matter of how it is registered. You can’t change the way an event is routed and so there are essentially three predefined classes.

You can usually tell that an event is tunnelling because its name starts with “preview” and a tunnelling event often comes paired with a bubbling event. However there is no such obvious way to discover if an event is bubbling or direct. If you are really stuck, or need to determine the type of an event at runtime, it is worth knowing that you can use the RoutingStrategy property as in:

Button.ClickEvent.RoutingStrategy;

together with the RoutingStrategy static enumeration to find out exactly how an event will be routed.

Tunnelling Events

As you might guess from the use of the standard naming convention for tunnelling events – i.e. Previeweventname – they are a way of allowing components to take a sneak look at what is happening before allowing lower level components to process the event. To achieve the same result without tunnelling events you need to use techniques that let you take a peak at an input stream, or whatever, before allowing the usual object that processes the stream to work with it. This all makes better sense after a simple example.

Place a textbox on the form and add the following event handlers:

this.AddHandler(
Keyboard.PreviewKeyDownEvent,
new RoutedEventHandler( MyKeyHandler));
textBox1.AddHandler(
Keyboard.PreviewKeyDownEvent,
new RoutedEventHandler( MyKeyHandler));

The event handler simply displays the element that invoked it:

void MyKeyHandler(object sender, 
RoutedEventArgs e)
{
MessageBox.Show(sender + " key down");
}

If you now run the program, select the textbox, and press a key you will first see that the Window1 event handler responds and then the textbox’s event handler. The Window1 event handler could examine the key code by changing from  RoutedEventArgs to KeyEventArgs and from RoutedEventHandler to KeyEventHandler:

this.AddHandler(
Keyboard.PreviewKeyDownEvent,
new KeyEventHandler(MyKeyHandler));
textBox1.AddHandler(
Keyboard.PreviewKeyDownEvent,
new KeyEventHandler(MyKeyHandler));
void MyKeyHandler(object sender, KeyEventArgs e)
{
MessageBox.Show(sender + " key down "+ e.Key);
}

The RoutedEventArgs is the base class for more specialised object that carry additional information about the routed event.

As well as the PreviewKeyDownEvent which tunnels there is also the KeyDownEvent which bubbles. Given they are both triggered by the same external event you can make use of them both and at the same time if you want to.

The key idea here is that the tunnelling event always gets priority and then the bubbling event is routed. For example, if you change the event handlers to:

this.AddHandler(
Keyboard.PreviewKeyDownEvent,
new KeyEventHandler(MyKeyHandler));
textBox1.AddHandler(
Keyboard.PreviewKeyDownEvent,
new KeyEventHandler( MyKeyHandler));
this.AddHandler(Keyboard.KeyDownEvent,
new KeyEventHandler(MyKeyHandler));
textBox1.AddHandler(Keyboard.KeyDownEvent,
new KeyEventHandler(MyKeyHandler));

and run the program again you will see first the event handler invoked on Window1, then Textbox1, then Textbox1 again and finally Window1.

You can see that this is very sophisticated but it is also potentially overly complex and could be dangerous. The order of invocation of the event handlers is well defined but any of them can set the Handled property to true which stops event routing both in the tunnelling and bubbling directions.

The reason that this works is that events use the same KeyEventArgs object within the event handler invocations. Once again you can also add event handlers so that they ignore the Handled property and execute in spite of an attempt to stop them.

Given an element hierarchy of reasonable depth this is a recipe for complexity!



Last Updated ( Thursday, 22 October 2020 )