WPF Concepts—Built-In Commands
From C# Online.NET (CSharp-Online.NET)—your free C# and .NET encyclopedia
| CSharp-Online.NET:Articles |
| .NET Articles |
| © 2007 Sams Publishing |
Built-In Commands
A command is any object implementing the ICommand interface (from System.Windows.Input), which defines three simple members:
-
Execute—The method that executes the command-specific logic
-
-
CanExecute—A method returningtrueif the command is enabled orfalseif it is disabled
-
-
CanExecuteChanged—An event that is raised whenever the value ofCanExecutechanges
-
If you want to create Cut, Copy, and Paste commands, you could define and implement three classes implementing ICommand, find a place to store them (perhaps as static fields of your main Window), call Execute from relevant event handlers (when CanExecute returns true), and handle the CanExecuteChanged event to toggle the IsEnabled property on the relevant pieces of user interface. This doesn't sound much better than simply using events, however.
Fortunately, controls such as Button, CheckBox, and MenuItem have logic to interact with any command on your behalf. They expose a simple Command property (of type ICommand). When set, these controls automatically call the command's Execute method (when CanExecute returns true) whenever their Click event is raised. In addition, they automatically keep their value for IsEnabled synchronized with the value of CanExecute by leveraging the CanExecuteChanged event. By supporting all this via a simple property assignment, all of this logic is available from XAML.
Even more fortunately, WPF defines a bunch of commands already, so you don't have to implement ICommand objects for Cut, Copy, and Paste and worry about where to store them. WPF's built-in commands are exposed as static properties of five different classes:
-
ApplicationCommands—Close,Copy,Cut,Delete,Find,Help,New,Open,Paste,Print,PrintPreview,Properties,Redo,Replace,Save,SaveAs,SelectAll,Stop,Undo, and more
-
-
ComponentCommands—MoveDown,MoveLeft,MoveRight,MoveUp,ScrollByLine,ScrollPageDown,ScrollPageLeft,ScrollPageRight,ScrollPageUp,SelectToEnd,SelectToHome,SelectToPageDown,SelectToPageUp, and more
-
-
MediaCommands—ChannelDown,ChannelUp,DecreaseVolume,FastForward,IncreaseVolume,MuteVolume,NextTrack,Pause,Play,PreviousTrack,Record,Rewind,Select,Stop, and more
-
-
NavigationCommands—BrowseBack,BrowseForward,BrowseHome,BrowseStop,Favorites,FirstPage,GoToPage,LastPage,NextPage,PreviousPage,Refresh,Search,Zoom, and more
-
-
EditingCommands—AlignCenter,AlignJustify,AlignLeft,AlignRight,CorrectSpellingError,DecreaseFontSize,DecreaseIndentation,EnterLineBreak,EnterParagraphBreak,IgnoreSpellingError,IncreaseFontSize,IncreaseIndentation,MoveDownByLine,MoveDownByPage,MoveDownByParagraph,MoveLeftByCharacter,MoveLeftByWord,MoveRightByCharacter,MoveRightByWord, and more
-
Each of these properties does not return a unique type implementing ICommand. Instead, they are all instances of RoutedUICommand, a class that not only implements ICommand, but supports bubbling just like a routed event.
The About dialog has a "Help" Button that currently does nothing, so let's demonstrate how these built-in commands work by attaching some logic with the Help command defined in ApplicationCommands. Assuming the Button is named helpButton, you can associate it with the Help command in C# as follows:
helpButton.Command = ApplicationCommands.Help;
All RoutedUICommand objects define a Text property containing a name for the command that's appropriate to show in a user interface. (This property is the only difference between RoutedUICommand and its base RoutedCommand class.) For example, the Help command's Text property is (unsurprisingly) set to the string Help. The hard-coded Content on this Button could therefore be replaced as follows:
helpButton.Content = ApplicationCommands.Help.Text;
| TIP |
The Text string defined by all RoutedUICommands is automatically localized into every language supported by WPF! This means that a Button whose Content is assigned to ApplicationCommands.Help.Text automatically displays "Ayuda" rather than "Help" when the thread's current UI culture represents Spanish rather than English. Even in a context where you want to expose images rather than text (perhaps on a ToolBar), you can still leverage this localized string elsewhere, such as in a ToolTip.
Of course, you're still responsible for localizing any of your own strings that get displayed in your user interface. Leveraging |
If you were to run the About dialog with this change, you would see that the Button is now permanently disabled. That's because the built-in commands can't possibly know when they should be enabled or disabled, or even what action to take when they are executed. They delegate this logic to consumers of the commands.
To plug in custom logic, you need to add a CommandBinding to the element that will execute the command or any parent element (thanks to the bubbling behavior of routed commands). All classes deriving from UIElement (and ContentElement) contain a CommandBindings collection that can hold one or more CommandBinding objects. Therefore, you can add a CommandBinding for Help to the About dialog's root Window as follows in its code-behind file:
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Help, HelpExecuted, HelpCanExecute));
This assumes that methods called HelpExecuted and HelpCanExecute have been defined. These methods will be called back at appropriate times in order to plug in an implementation for the Help command's CanExecute and Execute methods.
Listings 3.11 and 3.12 change the About dialog one last time, binding the Help Button to the Help command entirely in XAML (although the two handlers must be defined in the code-behind file).
Listing 3.11. The About Dialog Supporting the Help Command
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="AboutDialog" Title="About WPF Unleashed" SizeToContent="WidthAndHeight" Background="OrangeRed"> <Window.CommandBindings> <CommandBinding Command="Help" CanExecute="HelpCanExecute" Executed="HelpExecuted"/> </Window.CommandBindings> <StackPanel> <Label FontWeight="Bold" FontSize="20" Foreground="White"> WPF Unleashed (Version 3.0) </Label> <Label>© 2006 SAMS Publishing </Label> <Label>Installed Chapters:</Label> <ListBox> <ListBoxItem>Chapter 1</ListBoxItem> <ListBoxItem>Chapter 2</ListBoxItem> </ListBox> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button MinWidth="75" Margin="10" Command="Help" Content= "{Binding RelativeSource={RelativeSource Self}, Path=Command.Text}"/> <Button MinWidth="75" Margin="10">OK</Button> </StackPanel> <StatusBar>You have successfully registered this product.</StatusBar> </StackPanel> </Window>
Listing 3.12. The Code-Behind File for Listing 3.11
using System.Windows; using System.Windows.Input; public partial class AboutDialog : Window { public AboutDialog() { InitializeComponent(); } void HelpCanExecute(object sender, CanExecuteRoutedEventArgs e) { e.CanExecute = true; } void HelpExecuted(object sender, ExecutedRoutedEventArgs e) { System.Diagnostics.Process.Start("http://www.adamnathan.net/wpf"); } }
Window's CommandBinding can be set in XAML because it defines a default constructor and enables its data to be set with properties. Button's Content can even be set to the chosen command's Text property in XAML thanks to a popular data binding technique. In addition, notice that a type converter simplifies specifying the Help command in XAML. A CommandConverter class knows about all the built-in commands, so the Command property can be set to Help in both places rather than the more verbose {x:Static ApplicationCommands.Help}. (Custom commands don't get the same special treatment.) In the code-behind file, HelpCanExecute keeps the command enabled at all times, and HelpExecuted launches a web browser with an appropriate help URL.
|

