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.
This Sample assumes that you understand the basics of WPF RoutedCommands. If not read up on them in the Visual Studio help files, Code Project articles, etc. and let’s get into this!
WPF Rock Star Josh Smith and I have been having deep discussions on WPF business applications, designer & developer roles, MDI, MVC and RoutedCommands. Regardless of which specific topic we were exploring the discussion always seems to come back to decoupling.
- Decoupling of designers and developers
- Decoupling of UI and business logic
- Decoupling of UI and RoutedCommands
- Decoupling of MDI business forms from the host window
- Decoupling of … from …
Whenever I try new techniques or architectures I really want to be sure what is going on. This helps me take advantage of the benefits and stay away from any bumps in the carpet.
I’m looking at using RoutedCommands for the ToolBar controls in my WPF application. In fact, Josh has recently posted, Zen and the Art of WPF that covers using RoutedCommands. I highly recommand reading this post.
My application uses the WPF TabControl for MDI management. When the user selects a form from the menu, a new TabItem is created, that form is added to the TabItem.Content and the TabItem is then added to the TabControl.Items collection. Pretty simple actually.
When I use the word, “form” what I’m really talking about is a WPF UserControl that contains the controls that make up the UI of the form. Working with UserControls in Expression Blend is super simple which is one reason I use them to build my application. In a “real world” application the form UserControls are loaded from different assemblies, which make for easy large application partitioning. With the forms as UserControls with almost no UI code this simplifies the designer and developer roles and provides the ability to have clean separation if desired.
So I was thinking, if each form has 5 – 10 possible RoutedCommands and the user opened 10 forms (TabItems), then I could possibly have 100 RoutedCommand bindings active in my application. My thoughts starting drifting off to how often does the CanExecute method get called on a RoutedCommand? Will the CanExecute method get called for every form (TabItem) even though that form is not visible? Do you know the answers to these questions?
Oh, one last thing before we get into the meat. This is my first attempt at official MVC. While all my previous ASP.NET and WPF business applications had business logic execution code where it belongs, this is the first time I’m actually trying to do MVC and call it MVC. This application lacks the MVC Model piece. We are only implementing the Controller and the View.
Context Of This Article
If your application is small with a few forms then some of what I present may be overkill. These techniques to manage the number of active RoutedCommands bindings are targeted at business applications with the potential of many forms being open at once. Please keep this in mind when reading the article as the information presented is specifically targeted to these types of business applications where the developer feels it is desirable to manage the number of these bindings.
This Sample Covers
- Decoupling RoutedCommand execution from the UI
- MDI RoutedCommand Management
- Brief Look at WPF MDI using the TabControl
Decoupling RoutedCommand execution from the UI
This application has two UserControls that are used as forms, BetterWay and NotSoGoodWay. The only difference between the two is how the RoutedCommand bindings are managed. We will get to RoutedCommand binding manangement in a bit.
Below is the XAML and code for the BetterWay UserControl.
<local:UserControlBase x:Class="BetterWay" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TestCommandBindings"> <StackPanel> <StackPanel x:Name="layoutRoot"> </StackPanel> <TextBox Margin="10" Text="Hi Better Way" /> </StackPanel> </local:UserControlBase>
The buttons will be added at run-time to the StackPanel. The TextBox gives us a control to look at.
Public Class BetterWay Inherits UserControlBase 'this is my business layer controller Private _objController As New Controller Private Sub BetterWay_Initialized(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Initialized ButtonFactory(ApplicationCommands.CancelPrint) ButtonFactory(ApplicationCommands.Copy) ButtonFactory(ApplicationCommands.Cut) ButtonFactory(ApplicationCommands.Find) ButtonFactory(ApplicationCommands.Help) ButtonFactory(ApplicationCommands.[New]) ButtonFactory(ApplicationCommands.Paste) ButtonFactory(ApplicationCommands.Print) ButtonFactory(ApplicationCommands.PrintPreview) ButtonFactory(ApplicationCommands.Redo) ButtonFactory(ApplicationCommands.Save) ButtonFactory(Commands.CloseTabItem) End Sub Private Sub BetterWay_Loaded(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded 'This get called twice when initially loaded into the tab If Me.CommandBindings.Count = 0 Then Dim objBinding As CommandBinding For Each btn As Button In Me.layoutRoot.Children If btn.Command Is Commands.CloseTabItem Then 'this command is handled in the base class objBinding = New CommandBinding(btn.Command, New Input.ExecutedRoutedEventHandler( _ AddressOf CloseTabItemExecute), New Input.CanExecuteRoutedEventHandler(AddressOf _ MyBase.CloseTabItemCanExecute)) Else 'these commands are handled in the business layer objBinding = New CommandBinding(btn.Command, New Input.ExecutedRoutedEventHandler( _ AddressOf _objController.CmdExecute), New Input.CanExecuteRoutedEventHandler( _ AddressOf _objController.CmdCanExecute)) End If Me.CommandBindings.Add(objBinding) Next End If End Sub Private Sub BetterWay_Unloaded(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) Handles Me.Unloaded Me.CommandBindings.Clear() End Sub Private Sub ButtonFactory(ByVal cmd As RoutedUICommand) Dim btn As New Button btn.Command = cmd btn.CommandTarget = Me btn.Content = cmd.Text If cmd Is Commands.CloseTabItem Then btn.Foreground = Brushes.Red End If Me.layoutRoot.Children.Add(btn) End Sub ''' <summary> ''' In a real application, if the form is dirty, give the user the ''' option to cancel the command, perform a save and then close or ''' just close and loose changes. ''' </summary> Protected Overrides Sub CloseTabItemExecute(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) If Not _objController.IsDirty Then MyBase.OnCloseTabItem() End If End Sub End Class
Let’s start with the topic decoupling RoutedCommand execution from the UI. Look at the BetterWay_Loaded event handler. Since the TabItem loaded event gets called multiple times, we only want to add the CommandBindings if there are none so we test for this and only add them if they are not present.
When adding the CommandBindings, notice that the command execution for the custom command Commands.CloseTabItem takes place in the UI code and the other commands are handled in the MVC Controller or business layer.
I didn’t want my UI code cluttered with RoutedCommand event handlers if the only thing the event handler was going to do was pass it off to the Controller or business layer anyway. So I pointed the RoutedCommand directly to the Controller.
For the purpose of this Sample, I’m using the same Controller for all forms and the Controller only has one RoutedCommand event handler. I did this to make this demonstration simple. In a “real” application, you would define event handlers for each of your UI commands in the Controller. This is an example of decoupling the UI from the RoutedCommands and the execution of the RoutedCommand.
Some RoutedCommands should be handled by the UI code. You’re not breaking any rules by doing this. For example I’m handling the Close Tab button command in the UI code. This is here so that the command handler code can make a decision and then raise a custom RoutedEvent to tell the UI to close the TabItem.
You can’t raise RoutedEvents from the business layer and normally we don’t want the business layer talking directly to the UI. Instead the UI asks the Controller or business layer if business object IsDirty before raising the RoutedEvent to close the TabItem.
Josh has pointed out to me that many uses of MVC have the Controller talk to the View, but only via an interface. The View would implement some interface and the Controller talks to that.
MDI RoutedCommand Management
This topic was the root source of the Sample and why I wrote this demo applcation and then this article. I really wanted to find out how 50 or 100 CommandBindings from different TabItems would work together.
This is what I learned from this application. If you have multiple TabItems that contain a form with active CommandBindings, those RoutedCommand.CanExecute methods will get called often. This even applies to forms (TabItems) that are not visible (not the TabControl.SelectedItem.)
So I wanted to come up with a way to unhook the CommandBindings for forms that were not active. I spoke with Josh several times about this and we came up with the following solution.
Give the BetterWay_Unloaded event handler a look. This code clears the UserControls CommandBindings collection when it’s unloaded. The UserControl.Unloaded event fires when the TabControl.SelectedItem is changed by the user clicking on another TabItem.Header. For this business application I only want the CommandBindings loaded for the active TabItem. So by removing them when the TabItem is not the TabControl.SelectedItem we accomplish this.
In the NotSoGoodWay UserControl, I load the CommandBindings when the Button is created and do not clear the CommandBindings when the form is not active. When the NotSoGoodWay UserControl is loaded into the TabControl, its RoutedCommand.CanExecute methods get called even when the form is not active (its TabItem is not visible).
Note on CanExecute
You can see that WPF calls these methods often which is a good feature. When writing your CanExecute methods you should ensure that these methods run real fast. These methods are not really the place to be hitting the database for an answer. Simple and fast code is the way to go here.
To see this in action:
I created the below video and then changed some of the code after making the video and didn’t want to redo the video. However the video covers the important concept of RoutedCommand Binding Management.

The video links require Microsoft Silverlight 1.0. If you do not have it, you will be prompted to install it when you click one of the links. After the short installation is completed, close the browser window that you did the install in and re-click the video you want to watch. You can also download it here. Windows XP or Vista required.
RoutedCommand.CanExecute Video
Testing The Application
- Run the demo application using the Visual Studio Start Debugging command.
- Size and position the Visual Studio Output window so that you can see the output.
- From the menu select Customers.
- You will see the CanExecute output messages.
- Click on some commands and watch the output messages.
- Click on the Close Tab button. The TabItem will close.
- From the menu select Customers.
- From the menu select the Use Better Way menu item. This will toggle the check mark.
- From the menu select Sales.
- When Sales is loaded it will load the NoSoGoodWay UserControl.
- Click on some commands and watch the output messages. On the Sales messages will appear.
- Click on the Customers TabItem.
- Click on some commands and watch the output messages.
- You will see Customers and Sales CanExecute messages in the output window.
Controller – Business Layer
Public Class Controller Public ReadOnly Property IsDirty() As Boolean Get 'a real application will have logic for determining the state 'of the business object. Return False End Get End Property Sub CmdCanExecute(ByVal sender As Object, _ ByVal e As CanExecuteRoutedEventArgs) e.CanExecute = True Debug.WriteLine(DirectCast(sender, UserControl).Name & " can execute " & _ DirectCast(DirectCast(e.Command, System.Windows.Input.ICommand), _ System.Windows.Input.RoutedUICommand).Text) End Sub Public Sub CmdExecute(ByVal sender As System.Object, _ ByVal e As System.Windows.RoutedEventArgs) e.Handled = True Debug.WriteLine("---------------------------------------") Debug.WriteLine(DirectCast(sender, UserControl).Name & " executed " & _ DirectCast(DirectCast(DirectCast(e, _ System.Windows.Input.ExecutedRoutedEventArgs).Command, _ System.Windows.Input.ICommand), _ System.Windows.Input.RoutedUICommand).Text) Debug.WriteLine("---------------------------------------") End Sub End Class
This code is very straight forward. CmdCanExecute method returns True and displays a message in the debugger Output Window. The CmdExecute method handles the event and displays and message in the debugger Output Window.
In a “real” application each of these event handlers will actually call another method on the Controller to execute the code. The reason for this is that you want your Controller or business layer to be able to support Unit Testing. Josh has a fantasic Code Project article MVC to Unit Test in WPF that covers this topic.
I need to discuss a dependency that has been introduced into this code. The event handler is dependent on WPF. There are no RoutedEventArgs in ASP.NET, console applications or WinForms so these applications can’t use these handlers.
I’m spit balling here so my solution may change based on feedback. You have several options. You can place all the RoutedCommand event handlers in your UI code and then call methods on the Controller. A lot of MVC solutions do this. Or you can add an additional handler that WinForms, ASP.NET and console applications can all use. That handler will call the same method that each of the RoutedEvent handlers will call.
Why are you writing your code like this Karl? Because my application is a WPF application. I will have part of the application exposed on the web but only a very small percentage. So for the sake of having cleaner WPF UI code, I’m writing my UI and Controller code like this. You need to evaluate your application needs to determine what is the best course of action for you.
Brief Look at WPF MDI using the TabControl
This application gives you a glimpse of where I’m going with respect to my WPF Business Application series and the handling of MDI. You can access the series from my Table of Contents page.
When I first started working on MDI in WPF I came to the conclusion that the TabControl would provide a simple and user friendly way to manage multiple documents or forms. Nothing prevents the application from opening a modal or non-modal window on top of the application, but for the most part the application forms will be contained in the TabControl.
One issue that you have to deal with is how does the TabItem close itself? We don’t want the TabItem content travelling up the Visual Tree, finding a TabControl and removing its parent TabItem. For me the easiest method was for the TabItem content to raise a RoutedEvent that the TabControl would handle and close the TabItem by removing it from its Items collection.
Since each form (UserControl) would need the ability to close itself I put most of the code in the UserControlBase class and derive all form UserControls from this class.
Public Class UserControlBase Inherits System.Windows.Controls.UserControl Public Shared ReadOnly CloseTabItemRoutedEvent As RoutedEvent = _ EventManager.RegisterRoutedEvent("CloseTabItem", RoutingStrategy.Bubble, _ GetType(RoutedEventHandler), GetType(UserControlBase)) Public Custom Event CloseTabItem As RoutedEventHandler AddHandler(ByVal value As RoutedEventHandler) Me.AddHandler(UserControlBase.CloseTabItemRoutedEvent, value) End AddHandler RemoveHandler(ByVal value As RoutedEventHandler) Me.RemoveHandler(UserControlBase.CloseTabItemRoutedEvent, value) End RemoveHandler RaiseEvent(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) Me.RaiseEvent(e) End RaiseEvent End Event Protected Overridable Sub OnCloseTabItem() RaiseEvent CloseTabItem(Me, _ New RoutedEventArgs(CloseTabItemRoutedEvent, Me)) End Sub ''' <summary> ''' This is always true so it is implement here. ''' </summary> Protected Overridable Sub CloseTabItemCanExecute( _ ByVal sender As Object, _ ByVal e As CanExecuteRoutedEventArgs) e.CanExecute = True End Sub ''' <summary> ''' This must be overriden in deriving classes. ''' Making a base UserControl class MustInherit causes the Visual Studio designer ''' to blow up. There are workarounds but I don't like them. ''' This is just easier and it works great. ''' </summary> Protected Overridable Sub CloseTabItemExecute( _ ByVal sender As System.Object, _ ByVal e As System.Windows.RoutedEventArgs) Throw New NotImplementedException("This method must be overriden") End Sub End Class
The CloseTabItemExecute method must be overriden in deriving classes or the above execption will get thrown if the method is executed. Please read the comments above the method to understand why the UserControlBase is not an abstract or MustInherit class.
When the TabItem is ready to close itself, the deriving classes execute the OnCloseTabItem method and the CloseTabItemRoutedEvent is rasised.
The CloseTabItemRoutedEvent is handled in the application Window. You can also abstract some of the below TabControl code into a derived TabControl class. For this demo, I’m keeping here and simple.
Class Window1 Private Sub Close_Executed(ByVal sender As System.Object, _ ByVal e As System.Windows.Input.ExecutedRoutedEventArgs) Me.Close() End Sub Private Sub CloseTabHandler(ByVal sender As Object, _ ByVal e As RoutedEventArgs)
Me.tcApplicationForms.Items.Remove(CType(sender, UserControlBase).Parent) End Sub Private Sub CommandBinding_CanExecute(ByVal sender As System.Object, _ ByVal e As System.Windows.Input.CanExecuteRoutedEventArgs) e.CanExecute = True End Sub Private Sub DisplayTabItem(ByVal strHeaderText As String) Dim ti As TabItem = GetTabItemByHeaderString(strHeaderText) If ti Is Nothing Then ti = New TabItem ti.Header = strHeaderText If mnuUseBetterWay.IsChecked = True Then ti.Content = New BetterWay With {.Name = strHeaderText} Else ti.Content = New NotSoGoodWay With {.Name = strHeaderText} End If Me.tcApplicationForms.Items.Add(ti) End If Me.tcApplicationForms.SelectedItem = ti ti.Focus() End Sub Private Function GetTabItemByHeaderString(ByVal strHeaderText As String) As TabItem For Each ti As TabItem In Me.tcApplicationForms.Items If ti.Header = strHeaderText Then Me.tcApplicationForms.SelectedItem = ti Return ti End If Next Return Nothing End Function Private Sub Open_Executed(ByVal sender As System.Object, _ ByVal e As System.Windows.Input.ExecutedRoutedEventArgs) DisplayTabItem(e.Parameter.ToString) End Sub Private Sub Window1_Loaded(ByVal sender As Object, _ ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded EventManager.RegisterClassHandler(GetType(UserControlBase), _ UserControlBase.CloseTabItemRoutedEvent, New RoutedEventHandler(AddressOf CloseTabHandler)) End Sub End Class
In a “real world” application the main difference will be how the application decides which form to load in the DisplayTabItem code. In my applications the MenuItem.CommandParamenter has the assembly and class of the UserControl to load. I then use reflection to load the UserControl and then add it to the TabItem like I am above. There are several other considerations for TabItem MDI that I will get into great detail when I write that article for the WPF Business Application Series.
The DisplayTabItem method is multi-purpose in nature. If the TabItem is not loaded DisplayTabItem loads it. If the TabItem it is loaded DisplayTabItem it brings it into focus.
The CloseTabHandler removes the TabItem from the TabControl.Items collection.
The CommandBinding_CanExecute method set the CanExecuteRoutedEventArgs.CanExecute property to True since the two commands are always available.
Source Download
This Sample code is a Visual Studio 2008 solution. This code will run under Visual Studio 2005 but will require a little work on your part. Create a new VS 2005 WPF solution. Then import all the code and XAML files into the new solution and your up and running.
After downloading please change the extension form .doc to .zip. This is a requirement of WordPress.com
WPF TabControl MDI and CommandBindings Source
Closing
I hope that you can learn just a little bit more about WPF fom this article and the Sample Series.
Have a great day!
Just a grain of sand on the worlds beaches.
Posted by Karl 









