
Introduction
This issue suggests an implementation of extending WPF routed commands in a way that enables them interaction back with the command sources for synchronization purposes.
Background
At first glance, WPF commands looked like… sort of events, that's what I thought, till I read a bit more about them. WPF commands are a great way to separate the command logic from its trigger sources (command sources, the ones that invoke the commands) and customize different logic on various targets. Routed command bubbles and tunnels through the element tree allowing each element to define its ability to execute the command and execute it when all the proper conditions add up.
One of its big strengths are a set of ready to use commands which have a built in support as part of some of the controls for performing specific command functionality corresponding to the element it is occurring on.
While examining and using WPF commands, along with its benefits, its deficiencies started to come out.
The Problem
One main shortage and the topic of our article is the inability of the command source to get updates from the command itself, it just wasn't handled by Microsoft.
Let's use an example to clear things up. Take a simple player application for example, which allows the user to play a media file by either clicking a play/pause toggle button or selecting a play/pause entry from a menu bar. Since both scenarios run the same functionality, both controls hook up the same command which get handled in the same target, the player form in this case, which is also the visual parent of both controls. The first scenario of clicking the toggle button works like a charm. But in the second scenario of clicking the menu entry "Play/Pause" is a whole other story. While the media file plays the toggle button stays apathetic to the new condition. Not only that, WPF commands suggest no support for such a scenario. It doesn't allow any kind of back door to the command sources. This article addresses the aforementioned WPF commands deficiency and suggests an appropriate solution for most uses.
Solutions
There are several ways of solving/bypassing the problem. Let's review two main solutions which solve our problem while maintaining a loosely coupled UI.
1. Use Our Own Class as a Command Parameter
A known and spoken solution proposes sending a custom object as the command property which comprises a value for the command sources which actuated the command to use as a return value. After each concrete command execution, all concrete command sources compel updating. Further information on that approach can be found at Extending WPF Commands by Patrick Klug.
Just to point out the main pros and cons for comparison:
Pros
- Solves the problem
- Keeps the opportunity of using WPF supported commands
Cons
- Forces customization of each and every single control that functions as a command source
- Causes superfluous
CanExecute
method invocations (one for its official part in WPF commanding mechanism and the other for the spoken solution)
2. Customize RoutedCommand to Embrace a Return Value
This solution suggests a custom command that leverages the existing one in a way that enables a return value per command, meaning that each source control attached to a concrete custom command can be notified on changes and access the return value to refresh itself.
Pros
- Solves the problem
- Does not require custom controls
Cons
- Eliminates the ability to use WPF supported command
- Since commands are
static
, you can have only one return value per command. Can be overcome by using a custom object (maybe some kind of a collection) as the return value. But this is an infrequent scenario and won't be addressed on this issue.
The Solution
Since originally I used the propose design as a solution to a player we developed at work, I decided to exemplify it on a player common scenario, playing a media file (a virtual one :-))from either a play/pause toggle button or a play/pause menu entry. As opposed to the toggle button which changes its state correspondingly playing or paused, the menu stays as is regardless of the media file state.


Actually I use it on numerous and disparate scenarios using all kinds of command source controls combination involving two or more of them.
Like almost everything in life, this solution comprises three parts:
- Creating the custom command
- Making sure all command source controls give ear to return value changes
- Applying the return value to the command on command handling
And you're ready to go. Yes, it's that simple!!!
Creating the Custom Command
The engine that runs this solution is the CustomCommand
class which inherits the RoutedCommand
WPF class.
public class CustomCommand : RoutedCommand
{
public delegate void ReturnValueChangedHandler(object sender, EventArgs e);
public event ReturnValueChangedHandler ReturnValueChanged;
private object returnValue;
public CustomCommand(string Name, Type OnerType): base(Name, OnerType)
{
}
public object ReturnValue
{
get
{
return returnValue;
}
set
{
returnValue = value;
ReturnValueChanged(this, new EventArgs());
}
}
}
The idea is to expand the command so it would carry a return value along with it aka ReturnValue
and a ReturnValueChanged
event to notify to whoever wants to listen, in fact to the command sources that rely on it. As you can see, that event raises on each ReturnValue
set.
Make Sure All Command Source Controls Give Ear to Return Value Changes
Since in our example, the toggle button is the only command source that needs the command return value, we'll see just the following code snippet:
((CustomCommand) Commands.Commands.ToggleCommand).ReturnValueChanged += (sender, e) =>
{
toggleBtn.IsChecked =
(bool)((CustomCommand)Commands.Commands.ToggleCommand).ReturnValue;
};
Setting the Command Return Value on Command Handling
The only thing left is setting the appropriate return value on command execution.
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter == null)
((CustomCommand)e.Command).ReturnValue = isPlay = !isPlay;
else
((CustomCommand) e.Command).ReturnValue = isPlay = (bool)e.Parameter;
e.Handled = true;
}
Conclusion
As you can see, after only three simple steps you have the whole commanding return value mechanism ready to go.
Attached is a demo app for clarifications (VS 2008 project).
History
- 8th August, 2008: Initial post
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.