I’ve been doing a lot of research with the Command pattern lately at work. Here’s what I found: You’re doing yourself a disservice – and your team – if you don’t play the hell outta this thing!
It’s a very powerful design for non-trivial Windows Forms clients. What’s non-trivial? Anything that uses a menu, toolbar or context menu to show perform the same action. In fact it’s so useful and so powerful that I’m a little disappointed that Microsoft didn’t provide something like it in the framework itself. They’ve provided something in Avalon WPF, but I haven’t had the chance to play with it. I know they use it: Visual Studio and Office take advantage of it. (For example, think of how many ways you can copy and paste text.)
What are some of the advantages?
- Automation – how easy is it to automate tasks in Visual Studio with a macro? Or reassign a keyboard shortcut? Enough said.
- Completely decouples the UI from the business objects. It’s easy to say this but really hard to do it, especially if deadlines are tight. With commands, you can keep the UI and throw away the business objects or vice versa.
- Easily allows plugins or addins. Suppose you have a client with a unique look that you want to control, but you want to offer the ability to extend or introduce functionality to third parties. Commands allow that. It’s how Visual Studio Add-ins do it.
There are disadvantages, of course – every design has drawbacks:
- It complicates the application design. Every pattern complicates application design; they’re used because the benefits outweigh the cost. In fact, they should only be used when the benefits outweigh the costs.
- Using commands complicate your controls and forms. Suppose you have a button that will be used to execute a command. Do you subclass Button so that it contains a Command and override OnClick? Do you use a normal Button and handle the Click event in the form? Change Button with MenuItem. Repeat questions.
- Debugging is a little harder. There are features in .NET 2.0 to mitigate this one.
So what does a Command look like? Well, that’s largely up to you. The bare minimum would be one method that would allow you to perform the action associated with a Command:
public interface ICommand
{
void Execute();
}
All of your concrete commands would implement this interface and when the button or menu is clicked, call command.Execute(). Easy, eh? There is a whole lot more you can do with the shared interface, however. In fact, there is a lot of common code that you can take advantage of, which is why I prefer an abstract class, like so:
public abstract class Command
{
public string Key { get; protected set; }
public string DisplayName { get; protected set; }
public bool Enabled { get; }
public abstract void Execute();
}
All the properties would have an implementation, I leave it out because I’m only concerned with API right now. We still have that abstract Execute() method, but now we have a few properties that deserve some explanation. To further separate the UI from the business logic, if the text that the user sees is associated with the command rather than the UI, then you can easily replace the commands without modifying the UI at all. This is extremely powerful when you start dealing with collections of commands that can all be handled in the same way. So that explains DisplayName.
The Enabled property is fairly obvious: there are times when it would either be impossible or just plain wrong to perform an action; in those cases, you want to disable the UI so that the action cannot be performed. I’ll come back to the Enabled property in a second.
The Key property is for when Commands are grouped together in a collection. It makes sense to store most of your commands globally with the main form in a list or map. We’ll need a way to uniquely identify them so that we can retrieve them later. That’s what the Key property is for. Go to Tools -> Options in Visual Studio, select Keyboard. There you’ll see the keys for the commands in Visual Studio, no pun intended.
Now, by itself, the above Command is very powerful. However, we’re not done. Combine the above with Data Binding in Window Forms and you have to write almost no code at all. Data Binding is a very powerful technology, which you’ll know about if you’re one of the millions of ASP.NET developers out there. In .NET 2.0, Windows Forms apps got the same treatment. I can’t cover data binding in detail in this post, so I’ll just assume you know about it. To really take advantage of Data Binding I have to modify my Command class slightly:
public abstract class Command : INotifyPropertyChanged
{
bool enabled;
string key, displayName;
public event PropertyChangedEventHandler PropertyChanged;
public string Key
{
get { return key; }
protected set { key = value; }
}
public string DisplayName
{
get { return displayName; }
protected set
{
if (displayName != value)
{
displayName = value;
OnPropertyChanged(new PropertyChangedEventArgs(“DisplayName”));
}
}
}
public bool Enabled
{
get { return enabled; }
set
{
if (enabled != value)
{
enabled = value;
OnPropertyChanged(new PropertyChangedEventArgs(“Enabled”));
}
}
}
public abstract void Execute();
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
}
OK, I gave you the whole implementation. You’ll see that my Command class now implements the INotifyPropertyChanged interface. This allows the data binding code to update the control that has bound to the data when the data changes. You’ll note that DisplayName and Enabled will raise the PropertyChanged event. Therefore you can create a Button, hook it up to a Command with a minimum of code and let the power of data binding deal with turning the command on and off:
Button button = new Button();
Command command = new ArbitraryCommand(“My Data”);
button.DataBindings.Add(“Text”, command, “DisplayName”);
button.DataBindings.Add(“Enabled”, command, “Enabled”);
button.Click += delegate { command.Execute(); };
This technique is very powerful when your business objects change on their own without user interaction.
There are few things you have to note. The DisplayName property will have to be internationalized if your app is interested in the rest of the world, which will make the class more complex. Also, the Command pattern kind of falls down when you need to pass data to the command. You lose the polymorphism of the Command class when you have to either know the specific concrete type or what data must be passed to a particular command.
Still, if you are doing serious Windows Forms apps, you should consider the Command pattern.
Technorati tags: Windows Forms, patterns
Now playing: Santana – Just Feel Better (Feat. Steven Tyler Of Aerosmith)
To make simple commands without having to make a separate class for each command, you can create a default command that takes a delegate, like this:
public class ActionCommand : Command
{
ThreadStart fFunc;
public ActionCommand(ThreadStart func) { fFunction = func; }
public override void Execute() { fFunction(); }
}
And to use it:
Command myCommand = new ActionCommand(delegate
{
// code of the command
});
Tommy,
You’re absolutely right.
That’s a very effective way to create commands that are always enabled or are local to a single form, with access to all of the form’s variables.
However, it wouldn’t scale to business objects that are shared across forms. Suppose I had an app that had a notify icon with a context menu and a main form with two ways of performing an action that manipulated a remote object on a different machine. A full-fledged Command object would scale a lot better in that scenario.
Any guidance for binding these commands to a menustrip/menuitem for enabled/disabled?
This technique plays quite nicely with System.ComponentModel.IExtenderProvider too. You can use that interface to define a component which decorates your user interface controls with command keys, which reference back into a Dictionary of Command objects.
Thanks for giving a concrete example. I asked myself the same questions you did , (wire up in event handler or subclass button).
This is refreshing compared to all the other, over simplified examples on the webs.
Using INotifyPropertyChanged is good idea, yet another pattern (i.e. Observer). Be cautious to not raise OnPropertyChange on non-UI thread though.
Good day.
n fact it’s so useful and so powerful that I’m a little disappointed that Microsoft didn’t provide something like it in the framework itself => Smart Client Software Factory ….