Routed events

This article was originally published in VSJ, which is now part of Developer Fusion.
So you think you know how events work? An event is a special form of delegate, complete with special accessor functions. A list of methods, the delegate’s evocation list, is built up by clients adding the methods they would like to be called when the event is triggered. The object that raises the event simply calls the delegate and all of the methods in the evocation list are called in no particular order. The basic event is simple and efficient but it has now been augmented by a new type of event – the routed event.

The routed event was introduced along with WPF and it’s distinctly different. It borrows quite a lot from the way events are handled in a browser environment. Of course, given the attempted convergence of web and desktop development in .NET, this is no accident. However it doesn’t help to point out that we are doing things in a “browser” way if you don’t know how HTML handles events. So the first thing to do is explain what routed events are all about.

Standard events, i.e. events based delegates and the event keyword, are simple and usually efficient. However there are times when they don’t work in a way that integrates with the object hierarchy you have created. A standard event can have many clients, but each one has to “sign-up” to the event and provide an event handling method. The same event handling method can be used to handle multiple events, i.e. it can be part of the evocation list of multiple events, but this is about as sophisticated as things get. Imagine that you have a visible control on a form which you have arranged to handle a click event. Now imagine that you, later on in your development, place another visual component, a bitmap say, in front of the button. It now receives the click events that the button would have received. This is fine as long as you don’t want the button to still function for some reason and want a click on the bitmap to act as if it was a click on the button. If you do then it would be a good idea if the click event “bubbled” up from the image to the button and perhaps even up to higher levels. This is the idea of a routed event.

A routed event is one that has the potential not only to occur on the object that initially raises it but on objects related to that object – the event can be passed on. In this sense a routed event is a lot like an exception in the way it can be passed on until it finds an object that that can handle it. To achieve the same result using a standard event you would have to attach the button’s click handler to the bitmap’s click event – messy and inefficient.

Routed events are implemented by the WPF event handler and this means that you can’t use them if you aren’t using WPF. They are also only used by classes that derive from UIElement which includes most user interface controls. This reflects the basic idea that routed events are really all about what happens in the UI.

Simple bubbling

To see exactly what routed events are all about there is no better way than a simple example. Start a new WPF project and place a single StackPanel control containing three buttons. You can use the designer for this simple task but the equivalent XAML is:
<StackPanel Name="stackPanel1">
	<Button Name="button1">Button1
		</Button>
	<Button Name="button2">Button3
		</Button>
	<Button Name="button3">Button2
		</Button>
</StackPanel>
The XAML markup emphasises the fact that the buttons are all child nodes of the StackPanel. How events are routed is governed by the element tree, and while this exists no matter how you create the UI, it is most clearly evident in the way that the equivalent XAML is nested. When you use the designer then the nesting of elements is all a matter or the order that you create them in and what is selected as the active element at the time of creation.

The order of elements in the tree is important because when an event occurs on one of the elements that event can propagate up the element tree to the parent, then the parent’s parent and so on. This is called event “bubbling”. The element that the event originates on is called the source of the event, and for a normal CLR event it is the only element that would get the opportunity to call methods registered in the event’s evocation list. A routed event passes this opportunity up the element tree to the source element’s parents.

To see this event bubbling in action we need to add some event handlers. This can be done in a number of ways for a routed event. You can use the AddHandler helper method to add a routed event directly to the evocation list. For example to setup an event handler for button1:

button1.AddHandler(
	Button.ClickEvent,
	new RoutedEventHandler(
	MyButtonHandler));
The simplest place to put this instruction is in the Window constructor following the call to InitializeComponent. Notice that we have to supply the event that the handler is being added to, e.g. Button.ClickEvent, and we have to create a RoutedEventHandler delegate to wrap the MyButtonHandler method. For the sake of the example MyButtonHandler is defined to be:
void MyButtonHandler(object sender,
	RoutedEventArgs e)
{
	MessageBox.Show("Clicked");
}
Notice that this has the correct signature for a RoutedEventHandler i.e. void, object, RoutedEventArgs.

If you now run the program you will discover that clicking on button1 results in the MessageBox appearing to indicate that it has been clicked. However, as you might also expect, clicking on anything else does nothing at all.

To see that the event does bubble up to at least the next level in the element tree we need to add another event handler, but this time for the stackPanel:

stackPanel1.AddHandler(Button.ClickEvent,
	new RoutedEventHandler(
	MyButtonHandler));
This looks superficially like the previous use of AddHandler, but on closer inspection it’s a little odd. The stackPanel doesn’t actually support a Click event and what we are doing here is technically slightly more complicated than simply bubbling up the event. We are asking the stackPanel to handle the button’s click event and are “attaching” to it a foreign event. If the stackPanel did support a Click event we wouldn’t have to qualify the event as Button.ClickEvent. Apart from this subtlety the AddHandler works in the same way. Now if you run the program you will notice that buttons 2 and 3 also produce messageboxes when you click on them and button1 now produces two messageboxes! As promised, the button click bubbles up to the stackPanel. If you would like to see the event bubble up to the top-most level – a Window object in this case – then add:
this.AddHandler(Button.ClickEvent,
	new RoutedEventHandler(
	MyButtonHandler));
Now button1 produces three messageboxes, and the other two buttons produce two.

Although fairly obvious by this stage it is worth making clear that you can use the RoutedEventArgs parameter passed to the event handling method to determine the source of the event and many other specific details that enable your event handler to behave appropriately.

Adding and removing handlers

Although you can use AddHandler to add methods to the routed event’s evocation list the system provides alternatives that make a routed event look more like a standard CLR event. For example, you can write:
button1.Click +=
	new RoutedEventHandler(
	MyButtonHandler);
The += operator automatically calls the AddHandler method. Notice that you can only use this if the object supports the event and you can’t use it to attach an event to an object that doesn’t implement the event. For example, you can’t write stackPanel1.Click+= because there is no Click event for a stackPanel. When the += operator can be used, however, you can even drop the creation of a new delegate and write:
button1.Click += MyButtonHandler;
…but, of course, MyButtonHandler still has to have the correct signature.

If you want to use anonymous methods you most certainly can, as in:

button2.Click += delegate(object sender,
	RoutedEventArgs e)
{
	MessageBox.Show("Clicked");
};
You can even use the most up-to-date way of creating an anonymous method, the lambda expression:
button2.Click += (object sender,
	RoutedEventArgs e)=>
	{
		MessageBox.Show("Clicked");
	};
The result is the same no matter how you code it.

You can also add event handlers within XAML using syntax very similar to HTML:

<Button Name="button1"
	Click="MyButtonHandler">
	Button1
</Button>
You can even define an attached event in XAML:
<StackPanel Name="stackPanel1"
	Button.Click="MyButtonHandler" >
If you want to remove a routed event handler you can simply make use of the RemoveEventHandler method or the overloaded -= operator. Notice that you can only remove an event handler via code – there is no XAML equivalent.

Stop the bubbling

Suppose you don’t want the event to be handled at a higher level. In this case you can set the Handled property to true. The event arguments are passed to the event handler using RoutedEventArgs object and this is where you will find the Handled property. If you set this to true event bubbling is suppressed. Change the event handler in our example to read:
void MyButtonHandler(object sender,
	RoutedEventArgs e)
{
	MessageBox.Show("Clicked");
	e.Handled = true;
}
…and when you run the program you will discover that only one messagebox appears per click. Notice that buttons 2 and 3 still respond to the click as they allow their click event to bubble up to the stackFrame level where it is handled and bubbling stopped.

Stopping the bubbling of an event seems like a good idea until you are on the receiving end of a well-meaning control down the hierarchy that has decided that the event you want to handle with some additional behaviour is in fact completely handled. To help with this problem you can still handle events that have been set to handled. Sounds confusing and it does have the potential to degenerate into a mess. All you have to do to handle handled events is set the handledEventsTwo parameter in the AddHandler method. For example, to force the stackPanel to handle events that are marked as handled you can change its addition to the invocation list to:

stackPanel1.AddHandler(
	Button.ClickEvent,
	new RoutedEventHandler(
	MyButtonHandler),true);
Now if you run the program you will discover that the button1 produces two messageboxes corresponding to the handling once at the button level and once at the stackPanel level. Notice that there is no XAML equivalent.

How best to make use of these facilities? I don’t think you should ever mark an event as handled and deprive elements higher in the element tree an opportunity to add behaviour to the event. The documentation suggests that you should only mark an event as handled if your implementation does something significant or complete and the event needs no further handling. Of course the danger here is that you might think that it’s a significant and complete action but another developer might very well disagree – so use with restraint.

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 objects 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!

Creating your own routed events

As the element hierarchy plays such a role in routed events you can only create new routed events in classes that derive from UIElement. The simplest way to do this is derive a new class from an existing WPF UIElement such as a button:
public class MyButton : Button
{
To define a new even we first need to use the EventManager class to register the new event. This is how the system knows how to handle the event and in particular what sort of routing is to be applied. The RegisterRoutedEvent returns a RoutedEvent object which has to be stored as a static field for later use within the new control. For example, we use the m_MyEvent field to store the object:
public static readonly RoutedEvent
	m_MyEvent =
	EventManager.RegisterRoutedEvent(
	"MyEvent", RoutingStrategy.Bubble,
	typeof(RoutedEventHandler),
	typeof(MyButton));
The first parameter specifies the name of the event that clients will use to add handlers to the event. This name has to be unique within the new control. Next we specify the type of routing, bubble in this case, and the type of the event handler and the type of object that can fire the event. The event handler delegate, RoutedEventHandler in this case, specifies the signature of any method that is to be added to the invocation list.

Now that we have registered the event we have to create some of the infrastructure needed to use the event. First we need to create the event property and an add and remove method using event accessors:

public event RoutedEventHandler MyEvent
{
	add { AddHandler(m_MyEvent, value); }
	remove { RemoveHandler(m_MyEvent,
		value); }
}
These are automatically used by the system when you add and remove event handlers using the += and -= operators. Notice that the event property has to have the same name as you registered using the EventManager.

Finally we need a RaiseEvent method that can be used to trigger the new event:

void RaiseMyEvent()
{
	RoutedEventArgs newEventArgs = new
RoutedEventArgs(MyButton.m_MyEvent);
	RaiseEvent(newEventArgs);
}
The only tricky part here is that the RoutedEventArgs constructor accepts a RoutedEvent object and creates the correct set of arguments based on the information it contains but the Source and OriginalSource properties are null. However you don’t have to do anything about them because they are filled in correctly when you use the RaiseEvent method.

For simplicity we can connect the new event to an existing Button event:

protected override void OnClick()
{
	RaiseMyEvent();
}
Of course in a real situation you would have some complex set of conditions to be fulfilled before the new event was raised.

Now we can make use of the new MyButton class and its associated routed event. Start a new project, enter the definition of the new button class below the Window() class, after all it isn’t worth creating the class as a separate entity and add a stackFrame using the designer. Now we can create an instance of MyButton at runtime (again it isn’t worth adding it to the Toolbox for an example):

MyButton B1=new MyButton();
We need to set some minimal properties to ensure that it displays in a reasonable way:
B1.Width = 50;
B1.Height = 50;
B1.Content = "ClickMe";
…and we need to add it to the stackPanel’s Children collection;
stackPanel1.Children.Add(B1);
Given that it now has a position within the element hierarchy – it is a child element of the stackPanel – we can use the new routed event and expect it to be bubbled up to the stackPanel if we add:
stackPanel1.AddHandler(
	MyButton.m_MyEvent, new
	RoutedEventHandler(MyButtonHandler));
void MyButtonHandler(
	object sender, RoutedEventArgs e)
{
MessageBox.Show("My New Clicked Event");
}
Notice that when using AddHandler you have to use the static field to specify the type of event that is going to be attached to the stackPanel. Contrast this to adding an event handler to a control that supports the event directly, for example:
B1.MyEvent += (object sender,
	RoutedEventArgs e) =>
{
	MessageBox.Show("MyClicked");
};
Now when you run the program the new event will bubble up to the stackPanel as promised. You can change the nature of the routing as part of the registration using the EventManager to see how it all works.

Routed events – good or bad?

Clearly routed events are a flexible addition to our toolkit, but you have to stop and wonder if it really is worth the added confusion caused by supporting and having to learn about two different sorts of event. As most events are indeed tied to visual components it probably makes reasonable sense to think in terms of routed events at all times – or at least wherever possible. On the other hand the structural problems that can be caused by allowing events to be handled in such a free way are potentially serious.

Imagine that you are presented with some code that you have never seen before and your task is to discover what happens when a button is clicked? Clearly nothing happens as there is no event handler added within the code in the neighbourhood of the button’s creation. Where else should you look to find a handler? This problem is made worse by the split into XAML markup and code “behind” and by the fact that we no longer have events included in the properties window. Perhaps its time for the introduction of an “Events” explorer as part of Visual Studio.


Mike James’ programming career has spanned many languages, starting with Fortran, and he keeps up with the latest innovations by undertaking hands-on projects and editing VSJ.


Internals

WPF makes some deeper changes to the way events are implemented. Normally events are a wrapper for particular Windows messages being passed from one window to another. When you click on a button, which is just a special type of window, a message is generated which is passed to other windows. A single application will generally consist of lots of windows each with its own window handle Hwnd and message passing mechanism. WPF changes all of this ancient machinery. A WPF application has a single top-level window and all of the other visible entities are implemented as a graphic without a separate Hwnd. All event handling is performed by the WPF system and it has nothing at all to do with message passing apart from the initial message generated by the top-level window to indicate that the user has clicked some region of the window.

Does this change to the internal workings of the event system really matter? In most cases the answer is probably no – after all, it doesn’t generally make any difference how a click is turned into a software event. There are two areas where you need to be careful, however. The first is any interoperability that you plan to make use of between WPF and Windows forms. Windows forms and controls are full windows complete with window handles and message handling. To mix WPF and Windows forms involves putting elements into the entity tree that have Hwnds and this can be messy. The second area is any use of the traditional approach to automating the use of an application by sending it Windows messages. There is no point in trying to send a MouseDown message to a button in a WPF application because the button isn’t a window and messages can only be sent to windows (that have window handles). As this use of message passing was never an acknowledged standard way of doing anything, you can’t really complain that it is now, and increasingly in the future, broken – but there is no equivalent way of automating a WPF application unless it is designed into the code.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“If debugging is the process of removing software bugs, then programming must be the process of putting them in.” - Edsger Dijkstra