span.sup { vertical-align:text-top; }
Advanced WPF
Understanding Routed Events and Commands In WPF
Brian Noyes
Code download available at:RoutedWPF2008_09a.exe(175 KB)
This article discusses:
|
This article uses the following technologies: WPF |
Contents
Routed Events Overview
WPF Element Trees
Event Routing
Routed Events and Composition
Attached Events
Routed Commands Overview
Routed Command in Action
Command Routing
Defining Commands
Command Plumbing
Routed Command Challenges
Avoiding Command Problems
Going beyond Routed Commands
A Routed Handler Sample
One of the most daunting things about getting up to speed on Windows® Presentation Foundation (WPF) is that there are so many new constructs to master. Even simple things like Microsoft® .NET Framework properties and events have new counterparts in WPF with added capabilities and associated complexity—specifically dependency properties and routed events. Then there is all the brand new stuff, such as animations, styling, control templates, and routed commands. There is a lot to learn.
In this article I am going to focus on two very important items in the list of new WPF elements to master. These items—routed events and routed commands—are related to each other. They form the basis for communication among the various parts of your user interface—whether those parts are individual controls on one big Window class or whether they are controls and their supporting code in separate, decoupled parts of your user interface. For this article I am assuming you are already familiar with the fundamentals of WPF, such as how to construct a UI using built-in WPF controls and declaring the layout of your UI in XAML.
Routed Events Overview
When you are first getting started with WPF, you will likely use routed events without even knowing you are using them. For example, if you add a button to your window in the Visual Studio® designer and name it myButton and then double-click on it, the Click event will get hooked up in your XAML markup and an event handler for the Click event will be added to the codebehind of your Window class. This should feel no different than hooking up events in Windows Forms and ASP.NET. It is actually a little closer to the coding model for ASP.NET but more like the runtime model of Windows Forms. Specifically, in your XAML markup for the button, you end up with code like this:
<Button Name="myButton" Click="myButton_Click">Click Me</Button>
The XAML declaration for hooking up an event looks just like a property assignment in XAML but results in a normal event hookup on the object that specified the event handler. This hookup actually happens in a compile-time-generated partial class for your window. To see this, go to the constructor for your class, right-click on the InitializeComponent method call, and select Go To Definition from the context menu. The editor will display a generated code file (with a naming convention of .i.g.cs or .i.g.vb) containing the code that is normally generated at compile time. Scroll down in the displayed partial class to the Connect method where you will see the following:
#line 6 "..\..\Window1.xaml"
this.myButton.Click +=
new System.Windows.RoutedEventHandler(
this.myButton_Click);
This partial class is generated from the XAML at compile time and contains those elements of the XAML that need design-time compilation. Most of your XAML ends up as a binary-embedded resource in your compiled assembly and is merged at run time with the compiled code from the binary representation of your markup.
If you take a look at the codebehind for the window, the Click handler looks like this:
private void myButton_Click(
object sender, RoutedEventArgs e) { }
So far this looks like any other .NET event hookup—you have an explicitly declared delegate hooked up to an event on an object, and the delegate points to a handling method. The only thing to tip you off that you are using routed events is the type of the event argument for the Click event, which is RoutedEventArgs. What is so special about routed events then? To understand that, you first need to understand the elemental composition model of WPF.
WPF Element Trees
If you start with a new window in a project and drag a button onto the window in the designer, you end up with an element tree in the XAML that looks like this (attributes omitted for clarity):
<Window>
<Grid>
<Button/>
</Grid>
</Window>
Each of these elements represents a runtime instance of a corresponding .NET type, and the declared hierarchy of elements forms what is called the logical tree. In addition, many controls in WPF are either ContentControls or ItemsControls, meaning they can have child elements. For example, a Button is a ContentControl, and it can have a complex child element as its content. You could expand the logical tree, as you see here:
<Window>
<Grid>
<Button>
<StackPanel>
<Image/>
<TextBlock/>
</StackPanel>
</Button>
</Grid>
</Window>
This results in a UI like that shown in Figure 1 .
Figure 1 Simple Window with Button Content
As you can imagine, the tree could start to take on multiple branches (another Button in the Grid), and the logical tree can grow significantly in complexity. The thing to realize about WPF elements in the logical tree is that what you see is not really what you get at run time. Each of those elements usually expands to a more complicated tree of visual elements at run time. In this example, the logical tree of elements expands to the visual tree of elements shown in Figure 2 .
Figure 2 Simple Window Visual Tree
I used a tool called Snoop ( blois.us/Snoop ) to see the elements of the visual tree shown in Figure 2 . You can see that the window (EventsWindow) actually wraps its content in a Border and an AdornerDecorator and presents the content inside of that with a ContentPresenter. The button does something similar, wrapping its content in a ButtonChrome object and presenting the content with a ContentPresenter.
When I click on my button, I may not actually be clicking on the Button element at all; I may be clicking on a child element in the visual tree, possibly even one that is not shown in my logical tree (such as the ButtonChrome). For example, say that I click the mouse on top of the image inside my button. The click really manifests itself initially as a MouseLeftButtonDown event inside the Image element. But somehow that needs to get translated into a Click event at the Button level. This is where the routing in routed events comes in.
Event Routing
Understanding a little about the logical and visual trees is important because routed events get routed based primarily on the visual tree. Routed events support a RoutingStrategy of Bubble, Tunnel, or Direct.
Bubble is the most common and means that an event will bubble (propagate) up the visual tree from the source element until either it has been handled or it reaches the root element. This allows you to handle an event on an object further up the element hierarchy from the source element. For example, you could attach a Button.Click handler on the enclosing Grid element instead of directly on the button itself. Bubble events just have names that indicate their action (for example, MouseDown).
Tunnel events go in the other direction, starting at the root element and traversing down the element tree until they are handled or reach the source element for the event. This allows upstream elements to intercept the event and handle it before the event reaches the source element. Tunnel events have their names prefixed with Preview by convention (such as PreviewMouseDown).
Direct events behave like normal events in the .NET Framework. The only potential handler for the event is a delegate that is hooked up to the event.
Usually if a Tunnel event is defined for a particular event, there is a corresponding Bubble event. In that case, the Tunnel event fires first, starting at the root and working its way down to the source element looking for a handler. Once it has been handled or has reached the source element, the Bubble event is fired, working its way up from the source element and looking for a handler. A Bubble or Tunnel event does not stop its routing just because an event handler is called. If you want to stop the bubbling or tunneling process, you mark the event as handled in your event handler using the event arguments you are passed:
private void OnChildElementMouseDown(object sender,
MouseButtonEventArgs e) {
e.Handled = true;
}
Once your handler marks an event as handled, it will not be raised to any more handlers. Well, that is only partially true. In reality, event routing continues behind the scenes, and you can explicitly hook up event handlers in code with an override of the UIElement.AddHandler method that has an additional flag to effectively say, "Call me even if the event has been marked as handled." You specify that flag with a call like the following:
m_SomeChildElement.AddHandler(UIElement.MouseDownEvent,
(RoutedEventHandler)OnMouseDownCallMeAlways,true);
The first parameter to AddHandler is the RoutedEvent you want to handle. The second is a delegate to the event-handling method (which will need to have the correct signature for the event's delegate). The third parameter indicates whether you want to be notified even if another handler has marked the event as handled. The element on which you call AddHandler is the one that will be watching for the event to flow by during routing.
Routed Events and Composition
Let's walk through how the Button.Click event comes into being to drive home why all this is important. As I mentioned before, a user will initiate a Click event with a MouseLeftButtonDown event on some child element in the visual tree of your Button, such as the Image in the previous example.
When the MouseLeftButtonDown event happens inside the Image element, a PreviewMouseLeftButtonDown event starts at the root and tunnels down to the Image. If no handlers set the Handled flag to true for the preview event, the MouseLeftButtonDown event then starts bubbling up from the Image element until it gets to the Button. The button handles that event, sets the Handled flag to true, and raises its own Click event. The sample code for this article includes an application with handlers hooked up throughout the routing chain to help you visualize this process.
The implications are quite powerful. For example, if I choose to replace the default button appearance by applying a control template that contains an Ellipse element, I don't have to do anything to ensure that clicks outside the Ellipse don't fire the Click event. Clicks just outside the edge of the Ellipse will still be inside the rectangular bounds of my button, but Ellipse has its own hit detection for MouseLeftButtonDown, and the empty portions of the button outside the Ellipse do not.
So only clicks inside the Ellipse raise the MouseLeftButtonDown event. It is still handled by the Button class to which the template is attached, so you will get predictable behavior from even your customized button. This is also an extremely important concept to remember when writing your own custom composite controls because you will likely need to do things similar to what Button is doing to handle events from the child elements that get placed inside your control.
Attached Events
In order to enable elements to handle events that are declared in a different element, WPF supports something called attached events. Attached events are routed events that support a hookup in XAML on elements other than the type on which the event is declared. For example, if you want the Grid element to listen for a Button.Click event to bubble past, you would simply hook it up like the following:
<Grid Button.Click="myButton_Click">
<Button Name="myButton" >Click Me</Button>
</Grid>
The resulting code in the compile-time-generated partial class now looks like this:
#line 5 "..\..\Window1.xaml"
((System.Windows.Controls.Grid)(target)).AddHandler(
System.Windows.Controls.Primitives.ButtonBase.ClickEvent,
new System.Windows.RoutedEventHandler(this.myButton_Click));
Attached events simply give you a little more flexibility in where you hook up your event handlers. But if the elements are contained in the same class (as in this example), it may not be apparent what difference it makes because, in either case, the handling method is still just a method on the Window class.
It matters in two ways. First, event handlers are called based on where the handling element is in the bubbling or tunneling element chain. Second, this lets you do something like handle events from objects that may be encapsulated down inside a control you are using. For example, you could handle Button.Click events like those shown on the Grid, but those Button.Click events could be bubbling out from inside of a user control contained in your window.
Tip: Event-Handler Naming
If you don't want to be stuck with the default naming convention for event handlers (objectName_eventName), simply type in your desired event-handler name, right-click on it, and click Navigate to Event Handler in the context menu. Visual Studio will then generate an event handler for the name you specified.
In Visual Studio 2008 SP1, the Properties window will have an events view similar to the one in Windows Forms, so you will be able to specify the event name there once you have SP1. But if you are already in the XAML, this is a handy way to get an explicitly named handler generated.
Generate Event Handler
Not all events are declared as attached events. In fact, most are not. But attached events can be quite handy when you need to do event handling somewhere else besides the control's source.
Routed Commands Overview
Now that you have seen routed events, you are ready to understand routed commands. WPF-routed commands give you a specific mechanism for hooking up UI controls such as toolbar buttons and menu items to handlers without introducing a lot of tight coupling and repetitive code into your application. Routed commands give you three main things on top of normal event handling:
- Routed command source elements (invokers) can be decoupled from command targets (handlers)—they do not need direct references to one another, as they would if they were linked by an event handler.
- Routed commands will automatically enable or disable all associated UI controls when the handler indicates the command is disabled.
- Routed commands allow you to associate keyboard shortcuts and other forms of input gestures (ink gestures, for example) as another way to invoke the command.
In addition, a specific flavor of routed command, the RoutedUICommand class, adds the ability to define a single Text property to be used for the text prompting of any controls that are invokers for the command. The Text property can also be localized more easily than visiting each associated invoker control.
To declare a command on an invoker, you simply set a Command property on the control that will fire the command:
<Button Command="ApplicationCommands.Save">Save</Button>
The Command property is supported by MenuItem, Button, RadioButton, CheckBox, Hyperlink, and a number of other controls.
For an element that you want to act as a command handler, you set up a CommandBinding:
<UserControl ...>
<UserControl.CommandBindings>
<CommandBinding Command="ApplicationCommands.Save"
CanExecute="OnCanExecute" Executed="OnExecute"/>
</UserControl.CommandBindings>
...
</UserControl>
The CanExecute and Executed properties of a command binding point to methods in the codebehind of the declaring class that are called during the command-handling process. The important thing here is that the command invoker does not need any knowledge of or reference to the command handlers, and a handler does not have to know what element is going to invoke the command.
CanExecute is called to determine whether or not the command should be enabled. To enable the command, set the CanExecute property of the event arguments to true, as shown here:
private void OnCanExecute(object sender,
CanExecuteRoutedEventArgs e) {
e.CanExecute = true;
}
A command is also enabled if there is a command handler with a defined Executed method but no CanExecute method (CanExecute is implicitly true in that case). The Executed method is where you take whatever action is appropriate based on that command being invoked. This may be saving a document, submitting an order, sending an e-mail, or some other action with which the command is associated.
Routed Command in Action
To make this more concrete and to quickly see the benefits of routed commands, let's look at a simple example. In Figure 3 you see a simple UI with two input textboxes and a toolbar button for performing a Cut action on the text in the textboxes.
Figure 3 Simple App with Cut Command Toolbar Button
To get this hooked up using events, you would need to define a Click handler for the toolbar button, and that code would need references to the two textboxes. You would have to determine which textbox has the focus and call appropriate clipboard operations based on the text selection in the control. You would also have to worry about enabling and disabling the toolbar button at appropriate times based on where the focus is and whether anything was selected in the textbox. Ugly, messy, tightly coupled code.
It doesn't sound too bad for this simple form, but what if those textboxes are now down inside a user control or a custom control and your window codebehind doesn't have direct access to them? You would either have to expose an API at your user control boundary to make hooking things up from the container possible or expose the textboxes publicly—neither approach is ideal.
With commands, all you need to do is set the Command property on the toolbar button to the Cut command that is defined in WPF and you are done:
<ToolBar DockPanel.Dock="Top" Height="25">
<Button Command="ApplicationCommands.Cut">
<Image Source="cut.png"/>
</Button>
</ToolBar>
You could now run the app and see that the toolbar button is initially disabled. After you select some text in one of the textboxes, the toolbar button becomes enabled and, if you click it, the text is actually cut to the clipboard. And it would work for any textbox anywhere in your UI. Wow—pretty cool, huh?
What is happening here is that the TextBox class implementation has a built-in command binding for the Cut command and encapsulates the clipboard handling for that command (and Copy and Paste as well) for you. So how does the command only get invoked on the in-focus textbox, and how does the message get to the textbox to tell it to handle the command? That is where the routed part of routed commands comes into play.
Command Routing
The difference between routed commands and routed events is in how the command gets routed from the command invoker to the command handler. Specifically, routed events are used under the covers to route messages between the command invokers and the command handlers (through the command binding that hooks it into the visual tree).
There could be a many-to-many relationship here, but only one command handler will actually be active at any given time. The active command handler is determined by a combination of where the command invoker and command handler are in the visual tree, and where the focus is in the UI. Routed events are used to call the active command handler to ask whether the command should be enabled, as well as to invoke the command handler's Executed method handler.
Usually, a command invoker looks for a command binding between its own location in the visual tree and the root of the visual tree. If it finds one, the bound command handler will determine whether the command is enabled and will be called when the command is invoked. If the command is hooked up to a control inside a toolbar or menu (or, more generally, a container that sets FocusManager.IsFocusScope = true), then some additional logic runs that also looks along the visual tree path from the root to the focus element for a command binding.
In the simple application from Figure 3 , what happens is this: because the Cut command button is in a toolbar, CanExecute and Execute are handled by the TextBox instance that has the focus. If the textboxes in Figure 3 were contained within a user control, then you would have an opportunity to set up a command binding on the window, the user control that contains the Grid, the Grid that contains the textboxes, or on the individual textboxes. Whichever textbox has the focus will determine the end of the focus path originating from the root.
An important thing to understand about the routing of WPF routed commands is that once a single command handler is invoked, then no other handlers will be called. So if the user control handles the CanExecute method, the TextBox CanExecute implementation will no longer be called.
Defining Commands
Both the ApplicationCommands.Save and the ApplicationCommands.Cut commands are two of many commands provided by WPF. The five built-in command classes in WPF along with some examples of the commands they contain are shown in Figure 4 .
Figure 4 WPF Command Classes
Command Class | Example Commands |
---|---|
ApplicationCommands | Close, Cut, Copy, Paste, Save, Print |
NavigationCommands | BrowseForward, BrowseBack, Zoom, Search |
EditingCommands | AlignXXX, MoveXXX, SelectXXX |
MediaCommands | Play, Pause, NextTrack, IncreaseVolume, Record, Stop |
ComponentCommands | MoveXXX, SelectXXX, ScrollXXX, ExtendSelectionXXX |
The XXX indicates a collection of operations such as MoveNext and MovePrevious. The commands in each class are defined as public static (Shared in Visual Basic®) properties so that you can easily hook them up. You can define your own custom commands easily by following the same approach. I'll show an example of this a little later.
You can also use these commands with a shorthand notation, like the following:
<Button Command="Save">Save</Button>
When you use this shortened version, a type converter in WPF will try to locate the named command against the collection of built-in commands. The net result in this case is exactly the same. I prefer to use the long-named versions to make the code more explicit and maintainable. Then there is no ambiguity as to where that command is defined. Even the built-in commands have some duplication between the EditingCommands and ComponentCommands classes.
Command Plumbing
Routed commands are a specific implementation of the ICommand interface defined by WPF. ICommand has the following definition:
public interface ICommand {
event EventHandler CanExecuteChanged;
bool CanExecute(object parameter);
void Execute(object parameter);
}
The built-in WPF command types are RoutedCommand and RoutedUICommand. Both of these classes implement the ICommand interface and use routed events I described earlier to do the routing.
A command invoker is expected to call CanExecute to determine whether to enable or disable any associated command invocation code. The command invoker can determine when to invoke that method by subscribing to the CanExecuteChanged event. In the RoutedCommand class, CanExecuteChanged is fired based on changes of state or focus in the UI. When the command is invoked, the Executed method is called and gets dispatched to the handler through a routed event along the visual tree.
Classes that support the Command property, such as the ButtonBase class, implement the ICommandSource interface:
public interface ICommandSource {
ICommand Command { get; }
object CommandParameter { get; }
IInputElement CommandTarget { get; }
}
The Command property associates the invoker with the command it will invoke. CommandParameter allows the invoker to pass some data along with the invocation of the command. The CommandTarget property lets you override the default routing based on the focus path and tell the commanding system to use the specified element as the handler of the command, instead of leaving it up to the routed event and focus-based determination of the command handler.
Routed Command Challenges
Routed commands work nicely for simple user interface scenarios, hooking up toolbar and menu items, and handling those things that are inherently coupled to the keyboard focus (such as clipboard operations). Where routed commands are insufficient, however, is when you start building complex user interfaces, where your command handling logic is in supportng code for your view definitions and your command invokers are not always inside of a toolbar or menu. This often comes up when using UI composition patterns such as Model View Controller, or MVC ( msdn.microsoft.com/magazine/cc337884 ), Model View Presenter, or MVP ( msdn.microsoft.com/magazine/cc188690 ), or Presentation Model, which is also known as Model View ViewModel in WPF circles ( msdn.microsoft.com/library/cc707885 ).
The problem when you get into that arena is that the enabling and handling logic for a command may not be part of the visual tree directly; rather, it may be located in a presenter or presentation model. Additionally, the state that determines whether the command should be enabled may have no relation to where the command invokers and views are in the visual tree. You may also have scenarios where more than one handler is valid at a given time for a particular command.
To see where you might get yourself in trouble with routed commands, take a look at Figure 5 . It is a simple window that contains a couple of user controls that represent views in an MVP or MVC pattern. The main window contains a File menu and toolbar with Save command buttons in them. The main window also has an input textbox at the top of the window, along with a Button that has its Command set to the Save command.
Figure 5 Composite User Interface
Tip: Hooking Up Anonymous Methods
In the code in Figure 6 I use a trick learned from my colleague Juval Lowy—hooking up an empty anonymous method to the delegate in its declaration:
Action<string> m_ExecuteTargets = delegate { };
By doing so, you never have to check for null before invoking the delegate because there will always be one no-op subscriber in its invocation list. You also avoid a potential race condition with unsubscribing in a multithreaded environment if you were doing null checking instead.
For more details on that trick, see Programming .NET Components, Second Edition , by Juval Lowy.
The rest of the UI is provided by two views, and each is an instance of a simple user control. The border color has been set differently for each user control instance in order to make it easy to see what portion of the UI they provide. Each of the user control instances has a Save button that has its Command property set to the Save command.
The challenge introduced by routed commands being tied to a location in the visual tree becomes apparent in this simple example. In Figure 5 , the window itself does not have a CommandBinding for the Save command. However, it does contain two invokers (the menu and toolbar) for that command. In this situation, I don't want the top-level window to have to know what to do when the command is invoked. I want to leave it up to the child views, represented by the user controls, to handle the command. The user control class in this example has a CommandBinding for the Save command, and that CommandBinding returns true for CanExecute.
However, in Figure 5 you can see that while the focus is at the window level in the textbox at the top, the command invokers at this level are disabled. Also, the Save buttons in the user controls are enabled even though the focus is not yet inside the user control.
If you change the focus to one of the textboxes inside one of the user control instances, then the command invokers in the menu and toolbar become enabled. But the Save button in the window itself does not become enabled. In fact, in this case there is no way to get the Save button next to the textbox at the top of the window to enable with normal routing.
The reason again has to do with the location of the individual controls. With no command handlers at the window level, while the focus is outside of the user controls, there is no command handler up the visual tree or on the focus path to enable the controls that are hooked up as command invokers. The command is therefore disabled by default as far as those controls are concerned. However, for the command invokers inside the user controls, the command is enabled because the handler is up the visual tree from them.
Once you switch the focus to a textbox inside one of the user controls, the user control, which is between the window and the textbox on the focus path, provides the command handler that enables the command for the toolbar and menu, because they check the focus path as well as the path from their location in the visual tree to the root. And there is no way to enable the button at the window level because it will never have a handler between it and the root.
If trying to follow all that visual tree and focus-path mumbo jumbo made your head hurt for this simple little example, imagine what you will have to go through to reason out command enabling and invoking for a seriously complicated UI with command invokers and handlers in many different locations in the visual tree. Let's just say the movie Scanners and exploding heads come to mind.
Avoiding Command Problems
To avoid the potential visual tree location problems with routed commands, you'll need to keep things simple. You will generally need to make sure your command handlers are on the same element or further up the visual tree from the element that will invoke the command. One way to do this is to inject a command binding at the window level by using the CommandManager.RegisterClassCommandBinding method from the control contributing a command handler.
The exception to this is if you implement a custom control that itself accepts keyboard focus (like a textbox). In that case, if you want to embed command handling on the control itself and that command handling is only relevant when the focus is on your control, then you can do so and it will work out just like the Cut command example shown earlier.
You can also overcome focus issues by explicitly specifying a command handler through the CommandTarget property. For example, for the window-level Save button that was never enabled in Figure 5 , you could change its command hookup to the following:
<Button Command="Save"
CommandTarget="{Binding ElementName=uc1}"
Width="75" Height="25">Save</Button>
In this code, the Button specifically sets its CommandTarget to a UIElement instance that has a command handler in it. In this case, it is specifying the element named uc1, which happens to be one of the two user control instances in the example. Because that element has a command handler that always returns CanExecute = true, the Save button at the window level becomes always enabled and will only call that control's command handler, regardless of where the invoker is relative to the command handler.
Going beyond Routed Commands
As a result of the limitations of routed commands, a number of companies building complex UIs with WPF have started using custom ICommand implementations that allow them to provide their own routing mechanisms, particularly ones that are not tied to the visual tree and that can support multiple command handlers.
Creating a custom command implementation isn't hard. You implement the ICommand interface on a class, provide a way for command handlers to hook up, and then do the routing when the command is invoked. You also must decide what criteria you will use for determining when to raise the CanExecuteChanged event.
A good starting point for creating a custom command is to use delegates. A delegate already supports invocation of a target method, and it also supports multiple subscribers.
Figure 6 shows a custom command class called StringDelegateCommand that uses delegates to allow multiple handlers to hook up. It supports passing a string argument to the handlers, and it will use the CommandParameter of the invoker to determine what message is passed to the handlers.
Figure 6 Custom Command Class
public class StringDelegateCommand : ICommand {
Action<string> m_ExecuteTargets = delegate { };
Func<bool> m_CanExecuteTargets = delegate { return false; };
bool m_Enabled = false;
public bool CanExecute(object parameter) {
Delegate[] targets = m_CanExecuteTargets.GetInvocationList();
foreach (Func<bool> target in targets) {
m_Enabled = false;
bool localenable = target.Invoke();
if (localenable) {
m_Enabled = true;
break;
}
}
return m_Enabled;
}
public void Execute(object parameter) {
if (m_Enabled)
m_ExecuteTargets(parameter != null ? parameter.ToString() : null);
}
public event EventHandler CanExecuteChanged = delegate { };
...
}
You can see I've chosen to use a Func<bool> delegate to hook up the handlers that will determine whether the command is enabled or not. In the implementation of CanExecute, the class loops through the handlers hooked up to the m_CanExecuteTargets delegate and sees whether any one handler wants to execute. If so, it returns true for the StringDelegateCommand to be enabled. When the Execute method is called, it simply checks to see if the command is enabled and, if so, invokes all the handlers hooked up to the m_ExecuteTargets Action<string> delegate.
To hook up handlers to the CanExecute and Execute methods, the StringDelegateCommand class exposes the event accessors shown in Figure 7 to allow handlers to simply subscribe or unsubscribe from the underlying delegates. Notice that the event accessor also gives you the opportunity to trigger the CanExecuteChanged event whenever a handler subscribes or unsubscribes.
Figure 7 Command Event Accessors
public event Action<string> ExecuteTargets {
add {
m_ExecuteTargets += value;
}
remove {
m_ExecuteTargets -= value;
}
}
public event Func<bool> CanExecuteTargets {
add {
m_CanExecuteTargets += value;
CanExecuteChanged(this, EventArgs.Empty);
}
remove {
m_CanExecuteTargets -= value;
CanExecuteChanged(this, EventArgs.Empty);
}
}
A Routed Handler Sample
I've got this class hooked up in a sample application in the code download. The sample has a simple view with a presenter behind it (along the lines of MVP, but without the model). The presenter exposes a presentation model to the view for data binding (you can think of a presentation model as sitting between a presenter and a view, whereas the model of MVP sits behind the presenter from the view). The presentation model typically exposes properties to which the view can data bind. In this case, it just exposes a single property for the command so that it can be easily hooked up in the XAML of the view through data binding:
<Window x:Class="CustomCommandsDemo.SimpleView" ...>
<Grid>
<Button Command="{Binding CookDinnerCommand}"
CommandParameter="Dinner is served!" ...>Cook Dinner</Button>
<Button Click="OnAddHandler" ...>Add Cook Dinner Handler</Button>
</Grid>
</Window>
The Binding statement will just look for a property on the current DataContext, named CookDinnerCommand, and will try to cast that to an ICommand if it finds one. The CommandParameter was mentioned before, and it is a way for the invoker to pass some data along with the command. In this case, notice that I just pass the string that will be passed to the handlers through the StringDelegateCommand.
The codebehind of the view (the Window class) is shown here:
public partial class SimpleView : Window {
SimpleViewPresenter m_Presenter = new SimpleViewPresenter();
public SimpleView() {
InitializeComponent();
DataContext = m_Presenter.Model;
}
private void OnAddHandler(object sender, RoutedEventArgs e) {
m_Presenter.AddCommandHandler();
}
}
The view constructs its presenter, gets the presentation model from the presenter, and sets that as the DataContext. It also has the button Click handler, which calls into the presenter, asking it to add a handler for the command.
Composite Events and Commands
I've been working with the Microsoft patterns & practices group this year to help develop Composite Application Guidance for WPF, which is a set of guidance for developing complex composite applications in WPF. This set of guidance contains libraries, called the Composite Application Libraries (CAL), offering services and helper classes for composite applications.
Read more about Composite Application Guidance for WPF in Glenn Block's article, "Patterns for Building Composite Applications with WPF," at msdn.microsoft.com/magazine/cc785479 .
Figure 8 shows this application in action. The first window is in the initial state with no command handlers hooked up. You can see that the first button (the invoker) is disabled because there are no command handlers. Then when you press the second button, it calls into the presenter and hooks up a new command handler. The first button is then enabled, and when you click it, it invokes the command handler to which it is loosely coupled through a data binding and the underlying command's subscriber list.
Figure 8 Custom Command Sample in Action
The presenter code is shown in Figure 9 . You can see that the presenter constructs a presentation model and exposes it to the view through a Model property. When AddCommandHandler is called from the view (in response to the second button Click event), it adds a subscriber to the CanExecuteTargets and ExecuteTargets events on the model. These subscription methods are simple methods located in the presenter that return true and display a MessageBox, respectively.
Figure 9 Presenter Class
public class SimpleViewPresenter {
public SimpleViewPresenter() {
Model = new SimpleViewPresentationModel();
}
public SimpleViewPresentationModel Model { get; set; }
public void AddCommandHandler() {
Model.CookDinnerCommand.CanExecuteTargets += CanExecuteHandler;
Model.CookDinnerCommand.ExecuteTargets += ExecuteHandler;
}
bool CanExecuteHandler() {
return true;
}
void ExecuteHandler(string msg) {
MessageBox.Show(msg);
}
}
This example shows that a combination of data binding, UI patterns, and custom commands can give you a clean, decoupled approach for commands without being tied to the limitations of routed commands. Because the command is hooked up in the XAML through a binding, you can even extend this approach to define your views with XAML only (no codebehind), use bound commands from the XAML to trigger actions in your presentation model, and initiate the action you would have normally done in codebehind from your presentation model instead.
You'll need a controller for constructing the views and providing them with their presentation model, but you would be able to write interactive views without the need for codebehind. If you have no need for codebehind, there is much less opportunity for you to add tightly coupled and untestable spaghetti code in your codebehind file, as so often happens in UI apps. This approach is just beginning to be explored in WPF. But it is definitely something to consider, and you should be on the lookout for more examples.
Brian Noyes is chief architect of IDesign, a Microsoft Regional Director, and a Microsoft MVP. He is the author of Developing Applications with Windows Workflow Foundation , Smart Client Deployment with ClickOnce , and Data Binding with Windows Forms 2.0 . He is also a frequent speaker at industry conferences worldwide. Contact Brian through his blog at briannoyes.net .