WPF Simplified Part 7: Routed Events

In plain old .NET, an event is based on the idea of pub-sub model, the publisher publishes an “event” and the subscriber is notified of the “event”. In WPF the idea is still the same, but updated to take into account the rather complex visual trees that often occur in WPF.

In WPF, when an event is raised on an element in the visual tree, the event will either travel up the visual tree (from the element till it reaches the root) or down the tree (from the root to the element). Which direction it will take depends on the event’s predefined “routing strategy”. What is meant by travel is that the same event will fire on the element’s parent and the parent’s parent and so on, if the event is moving up. That is, if you have a Button inside a Grid, and if a MouseRightButonDown event is raised on the Button, a MouseRightButtonDown event is then raised by the WPF framework on the Grid, because the MouseRightButonDown event moves up (“bubbles up”).

The advantage here is that a high-level visual element need not explicitly hook the same event on all of its descendants, it can hook into the event on itself and wait for the framework to raise the event on itself (just like the Grid hooks into it’s own MouseRightButonDown event and waits for the framework to raise a MouseRightButonDown event on the Grid). Descendants don’t need to explicitly notify parents when an event occurs.

Take this simple XAML,

<Window x:Class="WpfApplication1.Window1"
    Title="Window1" Height="300" Width="300">

        <Button Height="100" Width="100">
            <StackPanel Height="80" Width="80">
                <RadioButton Content="Click Me"/>

Adding routed event handlers to this code,

<Window x:Class="WpfApplication1.Window1"
    Title="Window1" Height="300" Width="300">

    <Grid MouseRightButtonDown="GridMouseLeftButtonDownHandler"
        <Button Height="100" Width="100" 
            <StackPanel Height="80" Width="80" 
                <RadioButton Content="Click Me" 

And in the code behind class add handlers that look like this,

private void WindowMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Window caught event from " + e.OriginalSource.ToString());

private void GridMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Grid caught event from " + e.OriginalSource.ToString());
// Add similar handlers for Button, StackPanel, and RadioButton

private void WindowPreviewMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Window caught preview event from " + e.OriginalSource.ToString());

private void GridPreviewMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Grid caught preview event from " + e.OriginalSource.ToString());
// Add similar handlers for Button, StackPanel, and RadioButton

Now if you run this example, and click on the radio button’s circle, you’ll see the output look like this,

Window caught preview event from Microsoft.Windows.Themes.BulletChrome
Grid caught preview event from Microsoft.Windows.Themes.BulletChrome
Button caught preview event from Microsoft.Windows.Themes.BulletChrome
StackPanel caught preview event from Microsoft.Windows.Themes.BulletChrome
RadioButton caught preview event from Microsoft.Windows.Themes.BulletChrome
RadioButton caught event from Microsoft.Windows.Themes.BulletChrome
StackPanel caught event from Microsoft.Windows.Themes.BulletChrome
Button caught event from Microsoft.Windows.Themes.BulletChrome
Grid caught event from Microsoft.Windows.Themes.BulletChrome
Window caught event from Microsoft.Windows.Themes.BulletChrome

As you can see, the PreviewMouseRightButtonDown routed event is a tunneling event, which means it starts from the root (Window) and goes to the source (RadioButon), whereas the MouseRightButtonDown routed event is a bubbling event, which means that it starts at the source and goes to the root of the tree. By convention tunneling events have a prefix of Preview. There is also a third “routing strategy”, called “direct”, which means that the event is only raised on the element and does not travel either up or down the visual tree.

Input events are usually in pairs, like the MouseRightButtonDown and the PreviewMouseRightButtonDown both occur on a single user input. First the tunneling event is raised and travels its route (to the element from the root) and then the bubbling event is raised and it travels its route.

If you want to stop the tunneling or bubbling at any point (say at the Button level), simply set the Handled to true.

private void ButtonMouseLeftButtonDownHandler(object sender, MouseButtonEventArgs e){
    Debug.WriteLine("Button caught " + e.OriginalSource.ToString());
    e.Handled = true;

Notice in the output that Grid and Window don’t receive the bubbling MouseLeftButtonDown routed event.

To get all the routed events raised by a class, use the EventManager class,

var events = EventManager.GetRoutedEvents();
foreach (var routedEvent in events)
    EventManager.RegisterClassHandler(typeof(Window1), routedEvent, new RoutedEventHandler(handler));

internal static void handler(object sender, RoutedEventArgs e)
    Debug.WriteLine(e.OriginalSource + "=>" + e.RoutedEvent);

Writing a custom routed event is similar to writing a dependency property,

public static readonly RoutedEvent MyRoutedEvent = EventManager.RegisterRoutedEvent(
                               "MyRoutedEvent",            // Name of the custom routed event
                               RoutingStrategy.Bubble,     // The routing strategy
                               typeof(RoutedEventHandler), // Type of the event handler
                               typeof(MyControl));         // The type of the owner of this routed event 

// Provide CLR property wrapper for the routed event
public event RoutedEventHandler MyEvent
    add { AddHandler(MyRoutedEvent, value); }
    remove { RemoveHandler(MyRoutedEvent, value); }

The tunneling and bubbling of a routed event occurs when every element in the route exposes that event. But WPF supports tunneling and bubbling of routed events through elements that don’t even define that event – this is possible via attached properties. Attached events operate much like attached properties (and their use with tunneling or bubbling is very similar to using attached properties with property value inheritance), elements can handle events that are declared in a different element.

<Grid Button.Click="ButtonClickHandler">
    <Button Height="100" Width="100" Content="Some Text"/>

Grid doesn’t have a Click event, but WPF allows the Button.Click event to be raised on the Grid. The Grid can then handle this event in it’s handler “ButtonClickHandler”. Every routed event can be used as an attached event. We can also hook up the attached event in code,

<Grid x:Name="MyGrid">
    <Button Height="100" Width="100" Content="Some Text"/>
// In code...
this.MyGrid.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonClickHandler));

Digg This

About soumya chattopadhyay
I live and work in Seattle, WA. I work with Microsoft technologies, and I'm especially interested in C#.

3 Responses to WPF Simplified Part 7: Routed Events

  1. Pingback: WPF Simplified Series « I.Net

  2. Easy to understand. Thanks for the post.

  3. szumigalski says:

    VMware Certified Advanced Professional 6 (Desktop and Mobility Deployment) – The industry-recognized VCAP6-DTM Deploy certification validates that you know how to deploy and optimize VMware Horizon 6 (with View) environments. It proves that you have the knowledge and expertise essential to leverage best practices to provide a scalable and dependable Business Mobility platform for your organization. Some of the subjects include: Configuring and managing Horizon View components, configuring cloud pod archituecture, configuring Group Policy settings related to Horizon View, Configuring and optimizing desktop images for Horizon View & Mirage, Configuring and managing App Volumes AppStacks, Configuring desktop pools, Configuring and deploying ThinApp packaged applications, Configuring VMWare Identity Manager, etc.Szumigalski.com was designed by Sebastian to spread his passion for PowerShell & VMWare. Sebastian is an IT specialist employed in Singapore for longer than fifteen years who is routinely seeking brand-new solutions to sharpen his technical expertise & know-how. Since then, Sebastian has joined up with PowerShell User Group & VMWare VMug group, and has been participating in all of the meetings held in Singapore. This blog will disclose how Sebastian are able to systemize some of his day-to-day jobs by using PowerShell. You will discover study guides obtainable for the VCAP6-DTM examination, which are personally compiled by Sebastian. Sebastian is accredited with VCAP6-DTM, and is experienced with virtualization & server maintenance from four years experience of automation. The call for VMWare prepared admins and engineers are ever-increasing in the present technology industry. Become familiar more details on PowerShell & VMWare with Szumigalski.com!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: