Page 1 of 5
Routed events were new in .NET 3.5 but there is still plenty to discover about using them. We look at bubbling and tunneling and how to create your own routed even and how WPF changes the underlying mechanics of Windows.
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 - what is different?
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.
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:
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:
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,
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:
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:
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.