Whilst building the WPF version of our virtual tour software we came across a situation where routed commands, the built in command infrastructure in WPF would not actually achieve what we wanted. The way routed commands work is that they work their way up the visual tree trying to find a CommandBinding from someone who was interested in processing that command. Unfortunately in a number of situations in the viewer that is not the behaviour that we want. Sometimes you could be in a different part of visual tree but still want a command to execute when you press a keystroke for example.
Confused yet? Let's get on to a concrete example. Below is a screen shot of the virtual tour viewer running in WPF:
If the keyboard focus happened to be in the index for example you would still want the navigation buttons beside the word Introduction to be operational via the keyboard shortcut (Ctrl+Right):
Of course with a routed command it would simply try and find a binding from the current keyboard focus (index) up to the main window. As the command is handled by the TourContentControl on the right it would never know about the request.
Therefore we have come up with a new concept borrowed, in a way, from Expression Blend, called Targeted Commands. The way Targeted Commands work is that each window registers the commands they want to receive with the TargetedCommandManager. So when a command is executed the TargetedCommandManager goes through the list of registered bindings and finds out which window is interested in the current command. It can then use that given command binding to execute the command.
So, what do we want to achieve with this:
- Be able to send keyboard and button commands to the place that needs to be able to handle it
- Have the command automatically assign a tooltip to buttons which gives a description of the command and the keystrokes to execute it. Ultimately it would be good if the system could automatically set up the content of the button including an icon, which could be associated with the command (not done yet).
- Same code under Silverlight and WPF.
So let's look at some code. First off, how do we create a command?
public static readonly ICommand NextObjectCommand = new TargetedCommand("NextObject", typeof(MainWindow), new KeyGesture(Key.Right, ModifierKeys.Control));
As you can see it's very similar to a normal routed command. We create all commands in the main window. This allows us to ensure that all the possible keyboard shortcuts are a registered in one place which makes it easier to ensure that but we don't end up with duplicates. In a more line of business application it would allow you to give the user control over customizing the keyboard shortcuts because all the commands are created in one place, but it's not strictly required to do it that way.
In the constructor for the main window we create the TargetedCommandManager:
_commandManager = new TargetedCommandManager(this, null);
The second parameter, which is currently null allows you to provide a class implementing the interface ICommandReporter, which is an interface we have developed to provide support for reporting back to you the usage of your application in the real world.
Then in the control where you wish to handle the command, you declare a TargetedCommandBindingCollection:
private readonly TargetedCommandBindingCollection _bindings = new TargetedCommandBindingCollection();
Then in the constructor you can associate the commands with the binding collection:
Of course there is support for CanExecute, which ties into the existing CommandManager system of WPF, or in Silverlight with the CommandManager that is part of our Silverlight WPF compatibility library, which contains the Targeted Command System.
So the final part is to ensure that our bindings are registered with the TargetedCommandManager when the window is displayed. So we will create functions to add and remove the bindings:
internal void AddCommands()
internal void RemoveCommands()
So in this case when the TourContentControl (which is the larger window containing the title control) is displayed we call TitleControl.AddCommands(). We call TitleControl.RemoveCommands() when a different type of window appears in the content area of the MainWindow.
So now, in both WPF and Silverlight you automatically get the correct handling of the keyboard so that the command is routed to the correct place that actually wants to do something with it! Unfortunately, in Silverlight you don't actually get any ALT key commands (e.g: ALT+S) coming through to the keyboard handler as they are eaten by the browser (I’ll blog about that soon).
You assign the command to a button in exactly the same way as you always have, using the Command attribute. If you want the tooltip to automatically be created with a description pulled from the application resources using the name of the command, you can do it via the provided attached property (CommandHelpers.Command) which we have also created for Silverlight.
Here's an example of automatic tooltip creation, running under Silverlight:
So there we have one possible solution for handling commands in an application. Because of the design the TargetedCommandBinding could equally be put in the ViewModel rather than the View as we have done here.
The source code for the Targeted Command system and our routed command implementation for Silverlight and other associated classes will be available in the near future as part of the open source Silverlight WPF compatibility library we are creating. Keep watching for more news!