
Introduction
WPF Commanding offers the base for integration of the GoF Command pattern into an application. This article describes an approach with the following goals:
Program Architecture
- Creation of functional units in hierarchies
- Reduction of complexity in large applications
- Automated testing
- Reusability of the pattern and the commands
Command Description
- Description of a command should be neutral to the GUI in use
- Commands should be easy to localize
Command Runtime Behaviour
- Static parameter (per command instance)
- Context sensitive runtime data
- Source provided runtime parameters (
ICommandSource.CommandParameter
)
UI Integration
- Images and ToolTips with Gestures for existing command sources (
Button
and MenuItem
)
- Visualization of a Disabled state for
Button
controls
- Selectable binding via XAML or Code-Behind
- Integration of existing WPF commands as for example the
System.Windows.Documents.EditingCommands
Command Repository
- Centralizing the management of the commands
- Dynamic registration of commands
- Access to all commands
- Tracking of command execution
The attached example demonstrates how to use the Command pattern in an RTF editor:

The test project also shows how to unit test a command. The control CommandComboBox
shows how to implement a custom ICommandSource
.
Command
The class Command
, itself derived from RoutedUICommand
, provides the foundation for all commands. It serves as a common ancestor for developing individual command structures. The following overview shows some command classes of the RTF editor:

Note: This class hierarchy serves only for demonstrating command structures, and is not intended for production level usage of an RTF editor.
The implementation of a command class is compact and descriptive, and offers a well defined entry point for analysis in future extension steps:
public class EditUndo : EditorCommand
{
public EditUndo( Type ownerType, CommandDescription description ) :
base( "EditUndo", ownerType, description )
{
}
protected override bool OnCanExecute( EditorCommandContext commandContext )
{
if ( !base.OnCanExecute( commandContext ) )
{
return false;
}
return commandContext.TextBox.CanUndo;
}
protected override void OnExecute( EditorCommandContext commandContext )
{
commandContext.TextBox.Undo();
}
}
The class WrapperCommand
allows to integrate existing WPF commands. The following example shows how to reuse the system command EditingCommands.IncreaseIndentation
:
public class IncreaseIndentation : WrapperCommand
{
public IncreaseIndentation( Type ownerType, CommandDescription description ) :
base( EditingCommands.IncreaseIndentation, ownerType, description )
{
}
protected override bool OnCanExecute( CommandContext commandContext )
{
if ( !base.OnCanExecute( commandContext ) )
{
return false;
}
return true;
}
}
Command Runtime Behaviour
During the execution of a command, a command context is created which encapsulates all the runtime parameters. The following diagram documents the runtime behavior:
Creation of a CommandContext
instance is delegated to the Command
class itself, to support individual context types. The RTF editor example uses the classes ApplicationCommandContext
(error handling) and EditorCommandContext
(access to the RichTextBox
) for this purpose.
Note: Combining all the runtime values into the class CommandContext
enhances flexibility for future modifications: CommandContext
can be extended without having to update the method signatures of all existing commands.
Further down it will be shown how to use the CommandContext
for Unit Testing.
The property Command.Target
offers a way to set a parameter per command instance. Because the life cycle of a command is static, this Command.Target
property corresponds to a static
parameter.
UI Integration
Using the property Command.HasRequirements
(Default=True) allows to specify whether Enabling/Disabling is relevant for the command. If there are no requirements, the command is always active and Command.CanExecute()
will never be executed.
Button Command
The class ButtonCommandProvider
can be used, to attach a Command
to a Button
control:
XAML
<Button cmd:ButtonCommandProvider.Command="{x:Static rtf:RichTextEditorCommands.EditUndo}" />
Code-Behind
Button undoButton = new Button();
ButtonCommandProvider.SetCommand( undoButton, RichTextEditorCommands.EditUndo );
ButtonCommandProvider
offers various functionality aspects which can be activated/deactivated on an optional basis:
Button |
Functionality |
RoutedCommand |
Command |
Image |
Insert a button image (preview in Visual Studio XAML Designer) |
Using WrapperCommand |
Yes |
Enabling |
Visualization of the Disabled state |
Button-Content |
Button-Content |
ToolTip |
ToolTip according to the command description |
Using WrapperCommand |
Yes |
ToolTip Gesture |
ToolTip gesture according to the command description |
Yes |
Yes |
Display of the image can be controlled using the class ButtonImage
. The property ButtonCommandProvider.DisabledOpacity
determines how the content of an inactive button will be displayed.
The source of an image is determined by the class CommandImageService
. In addition to that, the property Command.ImageUri
offers a way to set another source for images. By default, button images are taken as:
- In PNG format
- Embedded resource in the assembly of the command
- Located in the 'Images' folder
The utility CommandToolTipService
controls the format of the button ToolTip.
MenuItem Command
Using the class MenuItemCommandProvider
can be used, to attach a Command
to a MenuItem
control:
XAML
<Button cmd:MenuItemCommandProvider.Command="{x:Static rtf:RichTextEditorCommands.EditUndo}" />
Code-Behind
MenuItem undoMenuItem = new MenuItem();
MenuItemCommandProvider.SetCommand( undoMenuItem, RichTextEditorCommands.EditUndo );
MenuItemCommandProvider
offers various functionality aspects which can be activated/deactivated on an optional basis:
MenuItem |
Functionality |
RoutedCommand |
Command |
Image |
Display an image along with the menu entry |
Using WrapperCommand |
Yes |
ToolTip |
ToolTip according to the command description |
Using WrapperCommand |
Yes |
ToolTip Gesture |
ToolTip gesture according to the command description |
Yes |
Yes |
Display of the image can be controlled using the class MenuItemImage
.
The format of the ToolTip is controlled by the class CommandToolTipService
.
Command Repository
The CommandRepository
offers access to all the commands and command bindings. The events CommandExecuting
and CommandExecuted
allow tracking of command executions. The command 'Command Statistics' in the 'Help' menu of the RTF editor is a sample implementation which simply lists all registered commands. The statusbar of the RTF editor displays information about the running command and the total number of executed commands.
Unit Testing
With the basis of the CommandContext
, we now have a way to realize individual test cases. The following testing method of the attached test project shows how to automate testing of a command:
[TestMethod]
public void TestToggleBoldDefault()
{
EditorCommandContext commandContext = CreateCommandContext();
TextRange startContent = new TextRange( commandContext.Document.ContentStart,
commandContext.Document.ContentEnd );
commandContext.Selection.Select( startContent.Start, startContent.End );
commandContext.Selection.ApplyPropertyValue( FlowDocument.FontWeightProperty,
FontWeights.Normal );
string startText = startContent.Text;
FontWeight defaultFontWeigth = RichTextEditorCommands.ToggleBold.BoldFontWeight;
RichTextEditorCommands.ToggleBold.Execute( commandContext );
TextRange endContent = new TextRange( commandContext.Document.ContentStart,
commandContext.Document.ContentEnd );
string endText = endContent.Text;
Assert.AreEqual( startText, endText );
object selectionFontWeight =
commandContext.Selection.GetPropertyValue( FlowDocument.FontWeightProperty );
Assert.AreEqual( defaultFontWeigth, selectionFontWeight );
}
private EditorCommandContext CreateCommandContext()
{
FlowDocument flowDocument = new FlowDocument();
for ( int i = 0; i < 10; i++ )
{
Paragraph paragraph = new Paragraph();
for ( int n = 0; n < 5; n++ )
{
paragraph.Inlines.Add( new Run( "Paragraph Text " +
n.ToString() + " " ) );
}
flowDocument.Blocks.Add( paragraph );
}
RichTextBox richTextBox = new RichTextBox();
richTextBox.Document = flowDocument;
return new EditorCommandContext( richTextBox );
}
Custom Command Control
According to the description in MSDN, the control CommandComboBox
shows the creation of a control which serves as a source for commands. Selection of a list entry will execute the command. To recognize whether the entry has been selected by the user (as opposed to a programmed way), the class UIElementInput
provides the relevant event information.
Usage
To use the command system, the following approach is suggested:
- Design and implement the command class hierarchy (optional: abstract classes and individual command contexts)
- Design and implement the command unit tests (optional)
- Create a
CommandCollection
: static binding of each command with a CommandDescription
- Embed button image resources (optional)
- Add
Command
references in XAML
- Configure the tooltip format upon application start (optional)
- Setup the
CommandRepository
at application start: CommandRepository.AddRange()
- Setup the
CommandBinding
in the application window: CommandBindings.AddRange()
Points of Interest
Further insights resulting from this article:
- Disabling a
ComboBox
depending on the state of an instance of the class CommandComboBox
- Content driven enabling: As soon as the font size reaches 25pt, the command
IncreaseFontSize
gets disabled
- Recognizing differing formats in the RTF selection:
EditFontSizeCommand.GetFontSize()
- Localization of a
CommandDescription
in RichTextEditorCommands
- Access to class values from XAML through the
DependencyProperty
of CommandWindow.CurrentCommandInfo
- Updating the statusbar during selection of a menu entry using a XAML
Style
EventSetter
with a call to CommandWindow.OnStateUpdate()
and CommandWindow.OnStatusReset()
- XAML Styling through class inheritance as in the examples
ButtonImage
and MenuItemImage
History
- 23th April, 2012 - v1.1.0.0
- Renamed
ButtonCommand
to ButtonCommandProvider
- Renamed
MenuItemCommand
to MenuItemCommandProvider
- ReSharped source code
- Refreshed article and fixed formatting
- 23th April, 2008
- Image for a
MenuItemCommand
- New classes
ImageButton
and MenuItemButton
- New class
WrapperCommand
- New section: Points of Interest
- 21th April, 2008