Introduction
This article discusses a technique for grouping together and executing multiple commands in sequence. I accomplished this by creating a class that implements WPF’s ICommand
interface, called CommandGroup
. That class has no inherent behavior or meaning, aside from grouping together a set of commands and treating them as an atomic unit. This design is similar to the ValueConverterGroup
class I created back in August of 2006.
Background
I recently had a conversation with Karl Shifflett, a fellow WPF Disciple, about a problem he faced in a WPF app. Suppose you have a TextBox
in a window, and a ToolBar
with a Save button in it. Assume the TextBox
’s Text
property is bound to a property on a business object, and the binding’s UpdateSourceTrigger
property is set to the default value of LostFocus
, meaning that the bound value is pushed back to the business object property when the TextBox
loses input focus. Also, assume that the ToolBar
’s Save button has its Command
property set to the ApplicationCommands.Save
command.
In that situation, if you edit the TextBox
and click the Save button with the mouse, there is a problem. When clicking on a Button
in a ToolBar
, the TextBox
does not lose focus. Since the TextBox
’s LostFocus
event does not fire, the Text
property binding does not update the source property of the business object.
Obviously, you should not validate and save an object if the most recently edited value in the UI has not yet been pushed into the object. This is the exact problem Karl had worked around, by writing code in his window that manually looked for a TextBox
with focus and updated the source of the data binding. His solution worked fine, but it got me thinking about a generic solution that would also be useful outside of this particular scenario. Enter CommandGroup
…
Introducing CommandGroup
My solution to the problem posed above is to give the ToolBar
’s Save button a command that executes two commands in sequence. First, I need a command to execute that updates the TextBox
’s Text
property binding so that the underlying business object has the new value entered by the user. After that has occurred, the Save command can execute with the assurance that the business object to be saved has the correct values. Here is the XAML in the demo application that creates the proposed solution:
<ToolBar DockPanel.Dock="Top">
<Button Content="Save">
<Button.Command>
-->
<local:CommandGroup>
<x:StaticExtension Member="local:FlushFocusedTextBoxBindingCommand.Instance" />
<x:StaticExtension Member="ApplicationCommands.Save" />
</local:CommandGroup>
</Button.Command>
</Button>
</ToolBar>
When the Save button is clicked, first my custom FlushFocusedTextBoxBindingCommand
will execute, forcing the focused TextBox
to update its data source. If the control with keyboard focus is not a TextBox
, that command immediately finishes, and all is well. Next, the standard Save
routed command will execute, allowing the application to do whatever needs to be done to validate and save the business object(s).
How CommandGroup Works
CommandGroup
implements the ICommand
interface, but in a rather unusual way. It merely delegates all calls to the CanExecute
and Execute
methods off to its child commands. Here is the code in CommandGroup
that provides it with a list of child commands:
private ObservableCollection<ICommand> _commands;
public ObservableCollection<ICommand> Commands
{
get
{
if (_commands == null)
{
_commands = new ObservableCollection<ICommand>();
_commands.CollectionChanged += this.OnCommandsCollectionChanged;
}
return _commands;
}
}
void OnCommandsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
this.OnCanExecuteChanged();
if (e.NewItems != null && 0 < e.NewItems.Count)
{
foreach (ICommand cmd in e.NewItems)
cmd.CanExecuteChanged += this.OnChildCommandCanExecuteChanged;
}
if (e.OldItems != null && 0 < e.OldItems.Count)
{
foreach (ICommand cmd in e.OldItems)
cmd.CanExecuteChanged -= this.OnChildCommandCanExecuteChanged;
}
}
void OnChildCommandCanExecuteChanged(object sender, EventArgs e)
{
this.OnCanExecuteChanged();
}
With that infrastructure in place, the ICommand
implementation is actually quite straightforward. You can see how I implemented it below:
public bool CanExecute(object parameter)
{
foreach (ICommand cmd in this.Commands)
if (!cmd.CanExecute(parameter))
return false;
return true;
}
public event EventHandler CanExecuteChanged;
protected virtual void OnCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
this.CanExecuteChanged(this, EventArgs.Empty);
}
public void Execute(object parameter)
{
foreach (ICommand cmd in this.Commands)
cmd.Execute(parameter);
}
As seen in the code above, the CommandGroup
class is simply a command container. If any of its child commands return false
from its CanExecute
method, CommandGroup
returns false
. When told to execute, CommandGroup
simply executes each of its child commands one after the next. Also, when any of its child commands raises the CanExecuteChanged
event, CommandGroup
bubbles that event up to WPF’s commanding system by raising its own CanExecuteChanged
event.
The Demo Application
When you run the demo application associated with this article, you will be see a ToolBar
with a Save button, and two TextBox
controls beneath it. Those TextBox
s are bound to a Foo
object, which has Name
and Age
properties. After you edit one of the TextBox
es, click on the Save button to see a MessageBox
that displays the current values in the Foo
object.
In the screenshot above, I added an exclamation mark to the Name
field and then clicked Save. As seen in the MessageBox
, the Foo
object’s Name
property is updated before the Save
command is executed. Here is the Save
command execution logic:
private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
{
Foo f = this.DataContext as Foo;
string msg = String.Format(
"Foo values:\r\nName={0}\r\nAge={1}",
f.Name,
f.Age);
MessageBox.Show(msg);
}
Here is the command class I wrote that finds the focused TextBox
and updates the source of the bound Text
property. This class could be extended to support finding various types of focused input controls, such as ComboBox
, if necessary:
public class FlushFocusedTextBoxBindingCommand : ICommand
{
#region Creation
public static readonly FlushFocusedTextBoxBindingCommand Instance;
static FlushFocusedTextBoxBindingCommand()
{
Instance = new FlushFocusedTextBoxBindingCommand();
EventManager.RegisterClassHandler(
typeof(UIElement),
Keyboard.PreviewGotKeyboardFocusEvent,
(KeyboardFocusChangedEventHandler)OnPreviewGotKeyboardFocus);
}
static void OnPreviewGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Instance.OnCanExecuteChanged();
}
protected FlushFocusedTextBoxBindingCommand()
{
}
#endregion
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
protected virtual void OnCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
this.CanExecuteChanged(this, EventArgs.Empty);
}
public void Execute(object parameter)
{
TextBox focusedTextBox = Keyboard.FocusedElement as TextBox;
if (focusedTextBox == null)
return;
BindingExpression textBindingExpr =
focusedTextBox.GetBindingExpression(TextBox.TextProperty);
if (textBindingExpr == null)
return;
textBindingExpr.UpdateSource();
}
#endregion }
Possible Improvements
You could enhance CommandGroup
in many ways, but I leave that as an exercise for the reader. One cool feature that pops into my mind is to have an IsAsync
property that, when set to true
, makes the CommandGroup
execute all child commands concurrently, on worker threads. This would be useful when the side-effects of running one command are irrelevant to the other commands.
Another useful thing to do is subclass CommandGroup
and give it child commands baked in by default. Suppose you want to have a custom logging command execute before any command executes, you could have a LoggingCommandGroup
that always executes a logging command first.
The possibilities are endless! :)
Revision History
- May 4, 2008 – Created the article.