Since the first event-driven language came down from the mountain and became part of the software development vernacular, there has been a certain notion of how an event works.
 |
Subscribe to event |
 |
Event fires |
 |
Deal with event |
 |
Breakfast! |
All in all, it’s pretty simple. And life was good.
So along comes WPF and things start to get a little more complicated. Events no longer have a simple one-to-one relationship. The event cycle may not be what you expect.
 |
Subscribe to event |
 |
Event fires |
 |
Events everywhere! |
 |
Deal with event |
 |
Events still running away! |
As I said, it’s not quite what you expect. Previous to WPF, event handling was very direct and closed. An event went where it was told and that was that. With WPF, events have a mind of their own, or so it would seem. But once you begin to understand what happens and why, WPF’s event-handling paradigm begins to make a lot more sense. And this paradigm is called Routed Events.
Manual versus Automatic
Windows Forms style event-handling is very manual. You subscribe to an event, deal with the event and it’s over, whether you like it or not. On one level, it means there are less pieces to worry about, less things that could go wrong. But the more you work with that style of event-handling, you realize how much your handcuffed by it.
To demonstrate the Windows Forms or CLR approach, I’ve made a Forms application to demonstrate how event processing occurred previously (and still does in non-WPF & non-Silverlight applications)

And the following code-behind:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.GotFocus += new EventHandler(button1_GotFocus);
button2.GotFocus += new EventHandler(button2_GotFocus);
tabControl1.GotFocus += new EventHandler(tabControl1_GotFocus);
}
void tabControl1_GotFocus(object sender, EventArgs e)
{
textBox1.Text += String.Format("tabControl1 GotFocus{0}", Environment.NewLine);
}
void button2_GotFocus(object sender, EventArgs e)
{
textBox1.Text += String.Format("Button2 GotFocus{0}", Environment.NewLine);
}
void button1_GotFocus(object sender, EventArgs e)
{
textBox1.Text += String.Format("Button1 GotFocus{0}",Environment.NewLine);
}
}
Pretty simple. When you it, you get:

All I did was tab through the controls and it printed exactly one line each time.
This is because normal events are “one and done” in the sense that once they are dealt with, they are considered to be handled and it’s all over.
Now let’s take a look what happens in WPF. I made a simple form in WPF (if anything in WPF can be considered simple, but this one actually is) and wired the GotFocus event for each of the controls, but I also wired it up for the form itself.

The XAML:
<Window x:Class="WPFEventDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" GotFocus="Window_GotFocus">
<Canvas>
<TabControl Canvas.Left="80" Canvas.Top="46" Height="100" Name="tabControl1" Width="200" GotFocus="tabControl1_GotFocus">
<TabItem Header="tabItem1" Name="tabItem1" GotFocus="tabItem1_GotFocus">
<Grid GotFocus="Grid_GotFocus">
<Button Content="Button1" Height="23" HorizontalAlignment="Left" Margin="20,11,0,0" Name="button1" VerticalAlignment="Top" Width="75" GotFocus="button1_GotFocus" />
<Button Content="Button2" Height="23" HorizontalAlignment="Left" Margin="20,39,0,0" Name="button2" VerticalAlignment="Top" Width="75" GotFocus="button2_GotFocus" />
</Grid>
</TabItem>
<TabItem Header="tabItem2" Name="tabItem2">
<Grid />
</TabItem>
</TabControl>
<ScrollViewer Height="116" Canvas.Left="28" Canvas.Top="172">
<TextBlock Name="tb" Text="" Width="446" />
</ScrollViewer>
</Canvas>
</Window>
And the code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_GotFocus(object sender, RoutedEventArgs e)
{
tb.Text += String.Format("Button1 GotFocus \n");
}
private void button2_GotFocus(object sender, RoutedEventArgs e)
{
tb.Text += String.Format("Button2 GotFocus \n");
}
private void Grid_GotFocus(object sender, RoutedEventArgs e)
{
tb.Text += String.Format("Tab 1 Grid GotFocus \n");
}
private void tabItem1_GotFocus(object sender, RoutedEventArgs e)
{
tb.Text += String.Format("tabItem1 GotFocus \n");
}
private void tabControl1_GotFocus(object sender, RoutedEventArgs e)
{
tb.Text += String.Format("tabControl1 GotFocus \n");
}
private void Window_GotFocus(object sender, RoutedEventArgs e)
{
tb.Text += String.Format("----------------------------\n");
}
}
And when you run it you get:

with the following in the TextBlock:
tabItem1 GotFocus
tabControl1 GotFocus
—————————-
Button1 GotFocus
Tab 1 Grid GotFocus
tabItem1 GotFocus
tabControl1 GotFocus
—————————-
Button2 GotFocus
Tab 1 Grid GotFocus
tabItem1 GotFocus
tabControl1 GotFocus
—————————-
Each section is the result of a single press of the tab key, including the dashed line. What you’re seeing is the result of event bubbling and this happens every time an event is fired in WPF and Silverlight. Look at the second group. When I tabbed to the button, it fired GotFocus for the deepest, nested object and then bubbled up doing likewise to each object in the tree until there were no more left. In fact, the GotFocus event for MainWindow is what added the dashed line at the end.
But what if you don’t want that behavior? That’s simple enough. You may have noticed that I haven’t been using the term “handle” for dealing with events. This was intentional, because “handled” has a specific meaning in RoutedEvent processing and now you’re going to learn what.
If you don’t want events to bubble up past a certain point, all you need to do is set Handled=true for RoutedEventArgs in the handler. At that point the bubbling stops and WPF truly considers the event completely handled. What is key is that I said “past a certain point”. You can stop the bubbling anywhere in the hierarchy, not just at the control that caused the event.
In the following code, I’ve set Handled to true at Button1 and tabItem1. Watch what happens.

Again, I tabbed 3 times. The first tab gave focus to tabItem1 and since I set Handled to true in tabItem1, that’s all it printed. The second tab went to Button1 and since Handled is true in that method, it also just prints one line. But I didn’t set anything in Button2 so when I tabbed to that control, it ran the handlers for Button1, the unnamed grid that is it’s parent and tabItem1, again stopping.
It’s important to note that if I had clicked tabItem2, it would have printed “tabControl1 GotFocus” and a dashed line because I never removed those handlers.
Conditional handling
I put the following code in the the tabControl1 GotFocus handler method:
private void tabControl1_GotFocus(object sender, RoutedEventArgs e)
{
Control osControl = e.Source as Control;
if (osControl.Name == "button1" || osControl.Name == "tabItem2")
{
e.Handled = true;
}
}
And what you’ve got is a very simple way of conditionally deciding if you want the event to keep bubbling all the way up the tree.
I hope I’ve given you enough information so that in your own events processing you can turn this…

into this…

(ALTERNATE VERSION for vegetarians)
I hope I’ve given you enough information so that in your own events processing you can turn this…

into this…

[Side note: Yes, I know I only talked about Bubbling and WPF also supports Tunneling. I’ll cover Tunneling in a future post!]
[Side note 2: Yes, I also know that Routed Events are not a new subject in the WPF and Silverlight world, but I was asked about them and it's never a bad time to go over the fundamentals.]

Popularity: 30%
Share and Enjoy:
These icons link to social bookmarking sites where readers can share and discover new web pages.