A command object identifies a particular command. It does not know how to handle a command—as we’ve seen, that’s the job of a command binding. Command objects are typically made available through static properties, such as ApplicationCommands.Properties.
There are several places from which you can get hold of a command object. Some controls define commands. For example, theScrollBarcontrol defines one for each of its actions, and makes these available in static fields, such asLineUpCommandandPageDownCommand. However, most commands are not unique to a particular control. Some correspond to application-level actions such as “new file” or “open.” Others represent actions that could be implemented by several different controls. For example,TextBoxandRichTextBoxcan both handle clipboard operations.
WPF provides a set of classes that define standard commands. These classes are shown in Table 4-4. This means you don’t need to create your own command objects to represent the most common operations. Moreover, built-in controls understand many of these commands.
Table 4-4. Standard command classes
Class
Command types
ApplicationCommands
Commands common to almost all applications. Includes clipboard commands, undo and redo, and document-level operations (open, close, print, etc.).
ComponentCommands
Operations for moving through information, such as scroll up and down, move to end, and text selection.
EditingCommands
Text editing commands such as bold, italic, center, and justify.
MediaCommands
Media-playing operations such as transport (play, pause, etc.), volume control, and track selection.
NavigationCommands
Browser-like navigation commands such as Back, Forward, and Refresh.
Although the standard commands cover a lot of the common features found in many applications, applications usually have functionality of their own not addressed by the standard commands. You can use the command system for application-specific actions by defining custom commands.
Example 4-17 shows how to define a custom command. WPF uses object instances to establish the identity of commands—if you were to create a second command of the same name, it would not be treated as the same command. Because commands are identified by their command objects rather than their names, commands are usually put in public static fields or properties.
Example 4-17. Creating a custom command
... using System.Windows.Input;
namespace MyNamespace {
public class MyAppCommands { public static RoutedUICommand AddToBasketCommand;
static MyAppCommands() { InputGestureCollection addToBasketInputs = new InputGestureCollection(); addToBasketInputs.Add(new KeyGesture( Key.B, ModifierKeys.Control|ModifierKeys.Shift)); AddToBasketCommand = new RoutedUICommand( "Add to Basket", "AddToBasket", typeof(MyAppCommands), addToBasketInputs); } } }
The first RoutedUICommand constructor parameter is the name as it should appear in the user interface. In a localizable application, you would use a mechanism such as the .NET class library’s ResourceManager to retrieve a localized string rather than hardcoding it. The second constructor parameter is the internal name of the command as used from code—this should match the name of the field in which the command is stored, with the command suffix removed.
As with the built-in commands, your application command doesn’t do anything on its own. It’s just an identifier. You will need to supply command bindings to implement the functionality. You will also typically want to associate the command with menu items or buttons.
Because this example uses a standard command from theApplicationCommandsclass, we can use this short form syntax, specifying nothing but the command name.
However, for commands not defined by the classes in Table 4-4, a little more information is required. The full syntax for a command attribute in XAML is:
[[xmlNamespacePrefix:]ClassName.] EventName
If only the event name is present, the event is presumed to be one of the standard ones. For example,Undois shorthand forApplicationCommands.Undo. Otherwise, you must also supply a class name and possibly a namespace prefix. The namespace prefix is required if you are using either custom commands, or commands defined by some third-party component. This is used in conjunction with a suitable XML namespace declaration to make external types available in a XAML file. (See Appendix A for more information on clr-namespaceXML namespaces.)
<Window xmlns:m="clr-namespace:MyNamespace;assembly=MyLib" ...> ... <Button Command="m:MyAppCommands.AddToBasketCommand"> Add to Basket</Button> ...
Because commands represent the actions performed at the user’s request, it’s likely that some commands will be invoked very frequently. It is helpful to provide keyboard shortcuts for these commands in order to streamline your application for expert users. For this, we turn to input bindings.
An input binding associates a particular form of input gesture, such as a keyboard shortcut, with a command. Two input gesture types are currently supported: a MouseGesture is a particular mouse input such as a Shift-left-click, or a right-double-click; a KeyGesture, as used in Example 4-16, is a particular keyboard shortcut. Many of the built-in commands are associated with standard gestures. For example,ApplicationCommands.Copyis associated with the standard keyboard shortcut for copying (Ctrl-C in most locales).
Occasionally, it can be useful to disable the default input bindings. A common reason for doing this is that a particular application may have a history of using certain nonstandard keyboard shortcuts, and you wish to continue this to avoid disorienting users. For example, email software has traditionally used Ctrl-F to mean “Forward,” even though this is more commonly associated with “Find” in other applications.
In most cases, you can just add a new input binding to your window, and that will override the existing binding. But what if you simply want to disassociate a particular shortcut from any command? You can do this by binding it to the specialApplicationCommands.NotACommandobject. Establishing an input binding to this pseudocommand effectively disables the binding.
The command source is the object that was used to invoke the command. It might be a user interface element, such as a button, hyperlink, or menu item. But it can also be an input gesture. Command sources all implement the ICommandSource interface, as shown in Example 4-20.
If you set theCommand property to a command object, the source will invoke this command when clicked, or in the case of an input gesture, when the user performs the relevant gesture.
TheCommandParameterproperty allows us to pass information to a command when it is invoked. For example, we could tell our hypotheticalAddToBasketcommand what we would like to add to the basket, as shown in Example 4-21.
Example 4-21. Passing a command parameter
<MenuItem Command="m:MyAppCommands.AddToBasketCommand" CommandParameter="productId4823" Header="Add to basket" />
The command handler can retrieve the parameter from theParameter property of theExecutedRoutedEventArgs, as Example 4-22 shows. (This example is a command handler for our hypotheticalAddToBasketCommand. The handler would be attached with a command binding as was shown in Example 4-16.)
Command parameters are slightly less useful if you plan to associate commands with keyboard shortcuts. Input bindings are command sources, so they also offer aCommandParameterproperty, but Example 4-23 shows the problem with this.
Example 4-23. Associating a command parameter with a shortcut
This adds an input binding, associating the Ctrl-Shift-B shortcut with ourAddToBasketCommand. TheCommandParameterproperty of the binding will be passed to the command handler just as it is when the input source is a button or menu item. But of course, it will pass the same parameter every time, which limits the utility—you might just as well hardcode the value into the command handler. So in practice, you would normally use command parameters only for commands without a keyboard shortcut.
If you were building a real application with shopping-basket functionality, it would probably make more sense to use data binding rather than command parameters. If you arrange for the control that invokes the command to have its data context set to the data you require, the command handler can retrieve theDataContextof the command target, as Example 4-24 shows.
The ICommandSourceinterface also offers aCommandTargetproperty. Although the interface defines this as a read-only property, all of the classes that implement this interface in WPF add a setter, enabling you to set the target explicitly. If you don’t set this, the command target will typically be the element with the input focus (although, as we’ll see later, there are some subtle exceptions).CommandTargetlets you ensure that a particular command source directs the command to a specific target, regardless of where the input focus may be. As an example of where you might use this, consider an application that uses aRichTextBoxas part of a data template (introduced in Chapter 1)—you might use this to allow the user to add annotations to data items in a list. If you provided a set of buttons right next to theRichTextBoxto invoke commands such asToggleBoldorToggleItalic, you would want these to be applicable only to theRichTextBoxthey are next to. It would be confusing to the user if she clicked on one of these while the focus happened to be elsewhere in her application. By specifying a command target, you ensure that the command only ever goes where it is meant to go.
Please check back next week for the conclusion to this article.