This is the next sample in the WPF Sample Applications Series. The purpose of the Sample Series is to provide concise code solutions for specific programming tasks. This sample provides a brief description of the problem, the solution and full source code.
I have been working on several WPF applications in parallel for some time now. All of my business applications have one main window. These applications also have the ability to display a modal dialog box when required. For example displaying the About Dialog Box or to allow the user to interact with the currently displayed task in some modal way, like looking up a value in a related table or editing values in related tables. Very common tasks in a business application.
As I was testing my application I noticed a very strange behavior in the application for the first time. Code was running in the main window, but the modal window was the window raising the RoutedEvent. I had always read and been taught that RoutedEvents tunnel and bubble in the current window and not other windows. That is true, except when the RoutedEventHandler is set up using EventManager.RegisterClassHandler.
If WPF RoutedEvents are new to you or you feel you need a short review, please check out the RoutedEvent Viewer on my blog. It provides an interactive Routed Event Viewer application for understanding WPF Routed Events. An understanding of RoutedEvents and the concept of events being handled is important for this short demonstration. Additionally I recommend reading the Routed Events Overview in the MSDN documentation.
Let’s get into this.
Test Bench UI
The UI is very straightforward. In the above image, I have opened the included sample program and clicked the Click Me button.
When the two buttons are clicked, the code handlers respond by displaying a message. In the case of the New Window button being clicked, a new instance of Window2 will be created and displayed. The CheckBox when Checked, will mark any events handled by Class Handler as Handled.
The Message: #, provides a simple way of viewing the order in which the event handlers were called by WPF. As you use this Test Bench UI, keep track of the Message #’s to understand what is going on.
In the above image, the New Window button was clicked. You can see the order in which the handlers were called and a new instance of Window2 was created and displayed.
Now things get VERY interesting. The Window 2 Button Clicked button, was clicked and handled in Window1. Notice Message 6. This is what caught me off guard and the purpose of this blog post. The Class handler is assigned to handle all ClickEvents owned by the Button class. Have a look at the below code EventManager.RegisterClassHandler line. This line of code allows the Window1 class to monitor Button.ClickEvents in its own window and other windows in the application. This is a great feature, unless you don’t expect it to work this way.
Private Sub Window1_Loaded(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded 'best fully understand WPF when using this EventManager.RegisterClassHandler(GetType(Button), _ Button.ClickEvent, New RoutedEventHandler(AddressOf WatchOutHandler)) 'notice that the CheckBox's Click event will be captured with 'this event handler because the CheckBox gets its ClickEvent 'from ButtonBase and not the CheckBox class. Me.AddHandler(Button.ClickEvent, _ New RoutedEventHandler(AddressOf WindowSafeHandler)) End Sub
The above Me.AddHandler statement goes along with the above image. The comments above the Me.AddHandler statement give the big picture. Notice that after clicking the CheckBox, Message 7 is displayed for the Window Handler, but not the Class Handler. Many WPF programmers like to handler RoutedEvents at a parent container level since it just makes sense to do this. This little application teaches us, to be on the look out for other controls that derive from the same base class that can raise the event we are watching for. So as programmers, we should not just assume that if we are listening for a Button.ClickEvent that a real button control actually raised the event. In the above case, it was a CheckBox control that actually raised the event.
In the above image the Click Me button was clicked, but only the Class Handler actually handled the event since it was marked as handled and the other event handlers down stream were not set up to handle, previously handled events. This is where the Routed Event Viewer application really shines by allowing you to experiment and fully understand the concept of Handled RoutedEvents.
Window1 XAML
<Window x:Class="Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="500"> <StackPanel Orientation="Vertical"> <Button Margin="10" Content="Click Me" ToolTip="Click to display message below" VerticalAlignment="Top" Click="Button_Click_1"/> <Button Margin="10" Content="New Window" ToolTip="Click to display a new window" VerticalAlignment="Top" Click="Button_Click" /> <CheckBox Margin="10" x:Name="chkHandleWatchOutEvents" VerticalAlignment="Top" Content="Handle Watch Out Event" /> <TextBlock Margin="10" VerticalAlignment="Top" x:Name="tbWatchOutMessages" /> <TextBlock Margin="10" VerticalAlignment="Top" x:Name="tbSafeMessages" /> <TextBlock Margin="10" VerticalAlignment="Top" x:Name="tbHandlerMessages" /> </StackPanel> </Window>
Window1 XAML VB
Class Window1 Private _intMessageNumber As Integer Private Sub WatchOutHandler(ByVal sender As Object, ByVal e As RoutedEventArgs) _intMessageNumber += 1 If TypeOf e.OriginalSource Is CheckBox Then 'this line of code will never run, but here to prove the point Me.tbSafeMessages.Text = String.Format( _ "Class Handler: CheckBox clicked at {0}, Message: {1}", _ Now.ToLongTimeString, _intMessageNumber.ToString) Else Me.tbWatchOutMessages.Text = String.Format( _ "Class Handler: {0} was clicked at {1}, Message: {2}", _ CType(e.OriginalSource, Button).Content.ToString, _ Now.ToLongTimeString, _intMessageNumber.ToString) End If e.Handled = Me.chkHandleWatchOutEvents.IsChecked.Value End Sub Private Sub WindowSafeHandler(ByVal sender As Object, ByVal e As RoutedEventArgs) _intMessageNumber += 1 If TypeOf e.OriginalSource Is CheckBox Then Me.tbSafeMessages.Text = String.Format( _ "Window Handler: CheckBox clicked at {0}, Message: {1}", _ Now.ToLongTimeString, _intMessageNumber.ToString) Else Me.tbSafeMessages.Text = String.Format( _ "Window Handler: {0} was clicked at {1}, Message: {2}", _ CType(e.OriginalSource, Button).Content.ToString, _ Now.ToLongTimeString, _intMessageNumber.ToString) End If End Sub Private Sub Window1_Loaded(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded 'best fully understand WPF when using this EventManager.RegisterClassHandler(GetType(Button), _ Button.ClickEvent, New RoutedEventHandler(AddressOf WatchOutHandler)) 'notice that the CheckBox's Click event will be captured with 'this event handler because the CheckBox gets its ClickEvent 'from ButtonBase and not the CheckBox class. Me.AddHandler(Button.ClickEvent, _ New RoutedEventHandler(AddressOf WindowSafeHandler)) End Sub Private Sub Button_Click(ByVal sender As System.Object, _ ByVal e As System.Windows.RoutedEventArgs) Dim obj As New Window2 obj.Show() End Sub Private Sub Button_Click_1(ByVal sender As System.Object, _ ByVal e As System.Windows.RoutedEventArgs) _intMessageNumber += 1 Me.tbHandlerMessages.Text = String.Format( _ "Button Handler: {0} was clicked at {1}, Message: {2}", _ CType(e.OriginalSource, Button).Content.ToString, Now.ToLongTimeString, _ _intMessageNumber.ToString) End Sub End Class
Close
As WPF developers we have 4 ways of adding RoutedEvent Handlers to our applications and each was demonstrated in this Test Bench Sample.
- In XAML
- At the class level
- At the container level
- At the control level
Understanding how and when each event handler will be called is vital to writing a WPF application. WPF provides the flexibility we as developers require. However, it’s incumbent upon developers to understand and harness that intrinsic power so that we can deliver stable, super cool applications.
Source Code: After downloading the source code you MUST change the file extension from .zip.DOC to .zip. This is a requirement of WordPress.com.
Hope you can learn just a little bit more about WPF from this article and the Sample Series.
Just a grain of sand on the worlds beaches.


