So in case you haven’t heard, a few years ago when Microsoft released the .Net Framework 3.0 it also introduced a new graphical subsystem that’s supposed to eventually replace it’s aging GDI based WinForms. This new framework is known as Windows Presentation Foundation (WPF). Some applications are currently using some of its features such as Microsoft Office 2007, Internet Explorer 7, and Windows Vista. WPF is a HUGE project, and attempting to explain what it’s all about could take years, so to see a quick demo that shows a small sliver of what is possible with this new foundation check out this link:
WPF DemoSo anyways, I’ve been messing around with WPF a little bit lately and I’m starting to absolutely love it. Everything is so extensible and very easy to use. I think that the hardest part about the whole thing is that there’s just so much to it and there’s a huge learning curve when you first start out (it took me about 8 hours to make a basic control for god’s sake), but once you eventually learn some of the basics, it’s a really cool, powerful system.
One thing that I especially liked that’s new in the .Net Framework 3.0 and WPF is the concept of routed events. Routed events are, essentially, events that are sent (i.e. routed) from one control to another through the control hierarchy. Each routed event has a routing strategy associated with it. The routing strategy tells the routed event where it should be routed to and in what way. There are two main routing strategies that I’ve used so far:
- Bubbling: I think bubbling is the easiest strategy to understand. Essentially, when a bubbling event is raised the event “bubbles” up through the hierarchy of controls. So, for example, assume a button exists inside a grid, and the grid exists inside a window kind of like in the following diagram:
When the button is clicked, a routed event will be raised and will bubble up through the control hierarchy. So, in this example, the button will raise the event, and then route it to the grid, and then the grid will route it to the window. This event can be caught at any level of this hierarchy.
- Tunneling: Tunneling is also a very basic concept, but can be extremely useful. Tunneling is essentially the opposite of bubbling. When a control raises an event, the event starts at the root of the hierarchy and is routed down to the control. Consider the same example as before with the button inside the grid, inside the window:
When the button is clicked, it first raises a PreviewMouseDown event. This event is sent down through the hierarchy, starting at the root, until it eventually reaches the button that called the event.
All routed events have a handled property. In most cases, when the handled property is set to true, any handlers for that event that have not already been called will be skipped. This can be overrided by setting the handledEventsToo property to true when the handler is added. Any handler that has handledEventsToo as true will handle all events of its type regardless of whether or not the event has already been handled.
Many of the WPF controls have events that are exactly the same except for their routing strategy. The tunneled version is usually with ‘Preview’ (ex. PreviewMouseDown) by convention. The two events both share the same handled property so if a preview event’s handled property was set to true at some point, then when the regular event is raised its handled property will also be set to true. Usually, the tunneled event is raised and handled in the same class. This took me a second or third read to understand fully. The reason that the tunneled event is raised and handled in the same class is to allow the preview event to be passed down through the entire hierarchy (remember that a tunneled event starts at the root and gets routed down) before the actual event is raised. This is a way to notify the parent controls that an event is about to be raised before it actually is raised. This may sound redundant but it can be an extremely useful. For example, suppose you have a button that closes a dialog that contains vital information. The dialog can be set up to intercept a Button.PreviewMouseDown event. When the event is intercepted, the dialog can use the routed event arguments to figure out which button was pressed, and it can know that the user wishes to close the window. The dialog can then create a prompt the user if they want to save their changes first and call the appropriate functions.
There are some very obvious benefits to this routed event architecture. The first pro to this type of event is object composition, which is specific to WPF. Since objects can be composed inside each other very easily in WPF, there is no way of telling which control an event was called on if the old, direct event handling methods are used. For example, if I button has an Image object inside of it and the user clicks on the image, the routed event would simply be a mouse down event. If the user does not want to add functionality for clicking on the image, the user could just intercept the tunneled event route and handle all button clicks before the event gets to the image, or catch the image click event and handle it as a button click.
Another great advantage to routed events is that there is no longer a need for event handlers for each control of a specific type. Suppose there are three buttons in a container (yes, no, cancel). The container only needs to handle a single Button.Clicked and it can use the routed event args parameter of the handler to figure out which button was clicked. This encapsulates all the code for an event into one handler. This can also be useful if there are many instances of the same type of object in a container that all do the same thing (i.e. close buttons on tabs).
So, routed events are awesome. Here’s a diagram that shows the basic path of a routed event (excluding some parts of the visual and logical trees):