For a command to be of any use, something must respond when it is invoked. Some controls automatically handle certain commands—the TextBox and RichTextBox handle the copy and paste commands for us, for example. But what if we want to provide our own logic to handle a particular command?
Command handling is slightly more involved than simply attaching a CLR event handler to a UI element. The classes in Table 4-4 define 144 commands, so ifFrameworkElementdefined CLR events for each distinct command, that would require 288 events once you include previews. Besides being unwieldy, this wouldn’t even be a complete solution—many applications define their own custom commands as well as using standard ones.
The obvious alternative would be for the command object itself to raise events. However, each command is a singleton—there is only oneApplicationCommands.Copyobject, for example. If you were able to add a handler to a command object directly, that handler would run anytime the command was invoked anywhere in your application. What if you want to handle the command only if it is executed in a particular window or within a particular element?
TheCommandBindingclass solves these problems. ACommandBindingobject associates a specific command object with a handler function in the scope of a particular user interface element. ThisCommandBindingclass offersPreviewExecutedandExecutedevents, which are raised as the command tunnels and bubbles through the UI.
Command bindings are held in theCommandBindings collection property defined byUIElement. Example 4-25 shows how to handle the ApplicationCommands.Newcommand in the code behind for a window.
Example 4-25. Handling a command
public partial class Window1 : Window { public Window1() { InitializeComponent();
CommandBinding cmdBindingNew = new CommandBinding(ApplicationCommands.New); cmdBindingNew.Executed += NewCommandHandler; CommandBindings.Add(cmdBindingNew); }
void NewCommandHandler(object sender, ExecutedRoutedEventArgs e){ if (unsavedChanges) { MessageBoxResult result = MessageBox.Show(this, "Save changes to existing document?", "New", MessageBoxButton.YesNoCancel);
if (result == MessageBoxResult.Cancel) { return; } if (result == MessageBoxResult.Yes) { SaveChanges(); } } // Reset text box contents inputBox.Clear(); } ... }
As well as supporting execution of commands, CommandBinding objects can be used to determine whether a particular command is currently enabled. The binding raises a PreviewCanExecute andCanExecutepair of events, which tunnel and bubble in the same way as thePreviewExecutedandExecuted events. Example 4-26 shows how to handle this event for the system-definedRedocommand.
Example 4-26. Handling QueryEnabled
public Window1() { InitializeComponent();
CommandBinding redoCommandBinding = new CommandBinding(ApplicationCommands.Redo); redoCommandBinding.CanExecute += RedoCommandCanExecute; CommandBindings.Add(redoCommandBinding); }
Command bindings rely on the bubbling nature of command routing—the top-level Window element is unlikely to be the target of the command, as the focus will usually belong to some child element inside the window. However, the command will bubble up to the top. This routing makes it easy to put the handling for commands in just one place. For the most part, command routing is pretty straightforward—it usually targets the element with the keyboard focus, and uses tunneling and bubbling much like normal events. However, there are certain scenarios where the behavior is a little more complex, so we will finish off with a more detailed look at how command routing works under the covers.
All of the built-in command objects use a class called RoutedUICommand, and you will normally use this if you define application-specific commands.* RoutedUICommand provides the mechanism for finding the right command binding when the command is invoked. This often needs to be determined by context. Consider Example 4-27 .
If the focus is in the text box when theCopycommand is invoked, the text box handles the command itself as you would expect, copying the currently selected text to the clipboard. But not all controls have an obvious defaultCopybehavior. If the command were invoked while the focus was in the listbox, you would need to supply application-specific code in order for the command to do anything.RoutedUICommandsupports this by providing a mechanism for identifying the command’s target and locating the correct handler.
The target of theRoutedUICommandis determined by the way in which the command was invoked. Typically, the target will be whichever element currently has the focus, unless the command source’sCommandTarget has been set. Figure 4-4 shows the controls and menu from Example 4-27. As you can see from the selection highlight, the TextBoxat the top had the focus when the menu was opened, so you would expect it to be the target of the commands. This is indeed what happens, but it’s not quite as straightforward as you might expect.
Figure 4-4. Comand targets and focus
RoutedUICommand tries to locate a handler using a tunneling and bubbling system similar to the one used by the event system. However, command routing has an additional feature not present in normal event routing: if bubbling fails to find a handler,RoutedUICommandmay try to retarget the command. This is designed for the scenario where commands are invoked by user interface elements such as menu or toolbar items because these present an interesting challenge.
Example 4-27 is an example of this very scenario. It has a subtle potential problem. While the menu is open, it steals the input focus away from theTextBox. It’s unlikely that the menu item itself is the intended target for a command—it’s merely the means of invoking the command. Users will expect theCopymenu item to copy whatever was selected in theTextBox, rather than copying the contents of the menu item. The menu deals with this by relinquishing the focus when the command is executed. This causes the focus to return to theTextBox, and so the command target is the one we expect. However, there’s a problem regarding disabled commands.
A command target can choose whether the commands it supports are enabled. ATextBoxenables copying only if there is some selected text. It enables pasting only if the item on the clipboard is text, or can be converted to text. Menus gray out disabled commands, as Figure 4-4 shows. To do this, a menu item must locate the command target. The problem is that the menu is in possession of the keyboard focus at the point at which it needs to discover whether the command is enabled; the appropriate command target is therefore not the focused item in this case.
TheRoutedUICommandclass relies on focus scopes to handle this situation. If aRoutedUICommandfails to find a command binding, it checks to see whether the initial target was in a nested focus scope. If it was, WPF finds the parent focus scope, which will typically be the window. It then retargets the command, choosing the element in the parent scope that has the logical focus (i.e., the last element to have the focus before the menu grabbed it). This causes a second tunneling and bubbling phase to occur. The upshot is that the command’s target is whichever element had the focus before the menu was opened, or the toolbar button clicked.
If you are using menus or toolbars, you don’t need to do anything to make this work, becauseMenuandToolBarelements both introduce nested focus scopes automatically. However, if you want to invoke commands from other elements, such as buttons, you’ll need to define the focus scope explicitly. Consider Example 4-28.
This associates two buttons with commands supported by aTextBox. And yet, as Figure 4-5 shows, the buttons remain disabled even when theTextBoxshould be able to process at least one of the commands.
Figure 4-5. Commands disabled due to missing focus scope
Now when the buttons attempt to locate a handler in order to choose whether they are enabled, the presence of the focus scope will cause the command routing to look for the element with the focus. If theTextBoxhas the logical focus, it will become the command target. As Figure 4-6 shows, this causes the buttons to reflect the availability of the commands correctly, and it means they invoke the command on the correct target when clicked.
Figure 4-6.Command enabled thanks to focus scope
We don’t have to use focus scopes to solve the problem in this particular example. You can use the more explicit, though slightly cumbersome, approach shown in Example 4-30.
Here, each button specifies its command target explicitly. This makes it absolutely clear what the target will be. However, it is more verbose, so the automatic command routing is often more convenient. And even if the thought of manually specifying the command target for every item in a menu doesn’t strike you as unbearable, command routing has the added benefit of working well when there are multiple potential command targets (e.g., multiple text boxes on a form) and you want the command to go to whichever one last had the focus.
The input handling techniques shown in this chapter all involve writing code that runs in response to some user input. If your reason for handling input is simply to provide some visible feedback to the user, be aware that writing an event handler or a custom command is likely to be overkill. It is often possible to create the visual feedback you require entirely within the user interface markup by using triggers. Triggers offer a declarative approach, where WPF does more of the work for you.
Any discussion of input handling in WPF would be incomplete without some mention of triggers. However, trigger-based input handling is radically different from the more traditional approach shown in this chapter, and it depends on aspects of WPF not yet described. Accordingly, it is dealt with later, in Chapters 8 and 9. So, for now just be aware of the two techniques and their intended usage: triggers are best suited for superficial responses, such as making a button change color when the mouse moves over it; event handling is appropriate for more substantive behavior, such as performing an action when the user clicks a button.
Where Are We?
Input is handled through events and commands, which use a routing system to allow simple uniform event handling regardless of how complex the structure of the user interface visuals might be. Input events are the lower level of these two mechanisms, reporting the exact nature of the user’s input in detail. Commands allow us to work at a higher level, focusing on the actions the user would like our applications to perform, rather than the specific input mechanism used to invoke the action.
* Ink is input written with a stylus, whether on a Tablet PC or a hand-held device, although the mouse can be used in a pinch.
* If you write custom elements, you should do the same. Chapter 18 describes how to do this.
* Capturing the mouse does not constrain its movement. It merely controls where mouse events are delivered.
* It is technically possible to provide a different class if you have special requirements. Command sources are happy to use any implementation of the ICommand interface, so you are not obliged to use the normal command routing mechanism. But most applications will use RoutedUICommand.