Click here to Skip to main content
Click here to Skip to main content

To bubble or tunnel basic WPF events

, 24 Sep 2012
Rate this:
Please Sign up or sign in to vote.
A description of basic UI event handling.

Introduction

WPF gives us a number of different mechanisms for handling events – they are bubbling, tunneling, and direct. These are all known as Routed events.

Recently I was asked to explain how WPF handles events to a graduate. This is my way of explaining the topic.

The types of events

Direct event

You are probably already used to the direct routed event. This is where the item itself handles the event that occurred A good example would be handling he "onClick" event of a mouse button in standard WinForms. This is where the event is raised in the GUI item and gets handled by said GUI element.

Bubbling Event

Now we all like some bubbles in one form or another. Bubbling happens when the event is not handled by the element (say a textbox) and the event "bubbles" its way up the UI containers which hold it. For example, let's say you have a window that contains a panel and inside that panel you have a grid and inside the grid you have a textbox. If the event is not handled by the textbox, then it moves, is passed or "bubbles" up to the grid level (as the grid contains the textbox), if it is not handled at that level then the event bubbles further up the "tree" (known as a visual tree) to the panel where it may or may not be handled. This process continues until it is handled or the event "escapes" the top most element.

Examples of a bubbling event would be something like a "MouseButtonDown" event. Or a "Keydown" event.

Tunneling

It would probably surprise your cotton white socks off that Tunneling is the opposite of Bubbling. So instead of an event going "up" the visual tree, the event travels down the visual tree toward the element that is considered the source. The standard WPF naming definition of a tunneling event is that they all start with "preview" for example "previewdownkey" and "previewmousebuttondown". You can catch them on their way to the "target" element and handle it. An example for this might be perhaps you have some controls inside a grid control and for some reason you have decided that no control within that grid will be allowed to have the letter "t" reach it.

I have no idea why you might do this. Perhaps you're just that kind of person. If you are, you're weird.  But read on, we shall see how to deal with this (the blocking of the keypress, not you being weird).

Event Pairing

Most of the bubbling events are paired with a Tunneling event. For example, the PreviewKeyDown (tunneling) key event is paired with the Keydown event (bubbling). When the tunneling event works its way down the visual tree and hits the element (say a button) from whence it originated, if its not handled there and then the button will then kick off a "Keydown" bubbling event that bubbles up the visual tree until it is handled itself (or not as the code may be).

You can find all the predefined UI events over there the following link http://msdn.microsoft.com/en-us/library/ms590079.aspx (make sure you select the correct .net Framework that you are working in!). Now, it doesn't take much work to figure out that any "PreviewSomethingHappening tunneling event is paired with a SomethingHappening bubbling event.

E.g.:

  • PreviewMouseDown is paired with MouseDown
  • PreviewMouseWheel is paired with MouseWheel
  • and so on.

Handling the event

Regardless of how you get your event, once you have your hands on it you can tell the event that it has been handled, and prevent it being passed along.

Each type of event handler passes a "RoutedEventArgs" object. Inside this object there is a property called "Handled". Set "Handled" to true and any event handlers further along the visual tree will no longer handle that particular event.

Earlier our example described a tunneling "Keydown" event heading towards a Textbox. If a containing ui element (the window, panel or grid) catches and handles the tunneling event (by setting the Handled property to true) then the Textbox will not receive the event and no matching bubbling event will be kicked off.

How do I catch and handle the event?

Time to look at some code. Download and open the attached sample that accompanies this article.

Here is the XAML for the Main Window.

Main Window XAML

This XAML describes a simple window consisting of a StackPanel, within a grid within a button (our example from above).

Have a look at the designer window, as you can see it looks rather uninteresting.

If you examine at the XAML you will see entries for "Keyboard.PreviewKeyDown" and entries for "Keyboard.KeyDown" for each of the containers (except the button itself).

You will also see that each of the controls except the button and textbox has entries for these two events.

Head over to the code behind in the mainwindow.xaml.cs file  (no super clever MVVM stuff here, just easy to follow code (like having a pint of beer wouldn't you say Chris?)

private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
    MessageBox.Show("Window_PreviewKeyDown");
    //e.Handled = true;
}

private void StackPanel_PreviewKeyDown(object sender, KeyEventArgs e)
{
    MessageBox.Show("StackPanel_PreviewKeyDown");
    //e.Handled = true;
}

private void Grid1_PreviewKeyDown(object sender, KeyEventArgs e)
{
    MessageBox.Show("Grid1_PreviewKeyDown");
    //e.Handled = true;
}

private void Grid1_KeyUp(object sender, KeyEventArgs e)
{
    MessageBox.Show("Grid1_KeyUp");
    //e.Handled = true;
}

private void StackPanel_KeyUp(object sender, KeyEventArgs e)
{
    MessageBox.Show("StackPanel_KeyUp");
    //e.Handled = true;
}

private void StackPanel_KeyDown(object sender, KeyEventArgs e)
{
    MessageBox.Show("StackPanel_KeyDown");
    //e.Handled = true;
}

private void Grid1_KeyDown(object sender, KeyEventArgs e)
{
    MessageBox.Show("Grid1_KeyDown");
    //e.Handled = true;
}

private void Window_KeyDown(object sender, KeyEventArgs e)
{
    MessageBox.Show("Window_KeyDown");
    //e.Handled = true;
}

In the code we see that each event handler will pop up a messagebox telling us where the message currently sits.

Build and run the application, click in the textbox - nothing will appear to happen.

Keep the focus on the window and press a key on your keyboard. Message boxes will appear as you clear each one.

The order they appear is;

Window_PreviewKeyDown -> StackPanel_PreviewKeyDown -> Grid1_PreviewKeyDown -> Grid1_KeyDown -> StackPanel_KeyDown -> Window_KeyDown

As you can see the tunneling events displayed first, then the bubbling events second. Hurray!

Handling the event

The next stage is to Handle the event. In each function you will see I have included the following (commented out) code;

e.Handled = true;

Your next exercise, dear reader, is to uncomment out the line in StackPanel_PreviewKeyDown so that the code looks like the following;

private void StackPanel_PreviewKeyDown(object sender, KeyEventArgs e)
{
    MessageBox.Show("StackPanel_PreviewKeyDown");
    e.Handled = true;
}

Build and run the application - what change in behaviour do you see?

Comment it back out and play around with the equivalent code  in the remaining sections. This should give you a good idea of how and when the events bubble or tunnel.

Finally, lets see if we can trap that letter "t" being pressed and stop it from reaching the textbox.

Examining the KeyEventArgs documentation we see that it is possible to figure out which key was pressed by examining  the keyboard code property (called "Key") belonging to the KeyEventArg.

We can then compare this to "Keys.T" in the following fashion:

if (Key.T == e.Key)
{
...
}

So a solution might look like:

if (Key.T == e.Key)
{
    e.handled = True;
}

Or for a one liner:

e.Handled ==  (Key.T == e.Key);

The exercise for you is to figure out where it fits.

Summary

In this article we have seen that in WPF user interface events can "tunnel", "bubble", or be direct. We have concentrated on the tunneling and bubbling events in and we have see how we can catch, handle the events or let them slide by.

We have  also learnt that we can prevent messages from reaching controls by trapping the tunneling event and setting the Handled property to true;

Links of interest

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

bryce
Software Developer (Senior)
Australia Australia
No Biography provided

Comments and Discussions

 
Questionexcellent Pinmemberlizhong huang13-Dec-13 21:09 
GeneralMy vote of 5 PinmemberSarabjot Singh Makkar19-Apr-13 21:17 
GeneralMy vote of 5 PinprotectorPete O'Hanlon25-Sep-12 1:53 
GeneralRe: My vote of 5 Pinmemberbryce25-Sep-12 2:59 
GeneralMy vote of 5 PinmemberPhil Hambling24-Sep-12 20:05 
GeneralMy vote of 5 Pinmembervasim sajad24-Sep-12 19:28 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 24 Sep 2012
Article Copyright 2012 by bryce
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid