Commands and controls in WPF

Windows Presentation Foundation controls are “lookless.” That is, the behaviour of the control is defined in code, but the appearance of the control is defined in a XAML template, and that template can be replaced by applications that want to customise the look of the control.

A consequence of this is that you can’t directly hook up elements of the control user interface to aspects of the behaviour. For example, suppose you’re writing a calendar control, and your default template includes “month back” and “month forward” buttons. In a Windows Forms user control design, you could hook up the Click events of these buttons to event handlers in the code, but that’s not a good design in WPF. In fact, WPF makes it pretty hard to do it at all.

Try it. Create a custom control, put a button into the default template, and assign a Click handler to the button. You’ll get a compiler error saying that a ResourceDictionary needs an x:Class attribute to support event handlers. The x:Class attribute tells the XAML compiler to create a partial code-behind class which you can extend with your event handlers. So now you can create a C# or Visual Basic file containing a partial class with the appropriate name, you can add your event handler to that, and now the control compiles. You’ve still got a problem writing your event handler, because you’re in a ResourceDictionary-derived class, not your control class, and the event is coming from the button, which doesn’t give you ready access to the control instance that you want to act on.

This alone should be a hint that this is the wrong way to go, but suppose that you manage to overcome these obstacles and implement your control using this approach. What happens when someone decides to retemplate it? Well, they put a button into their template, and now they have to create their own event handler. That’s not too painful if your user is defining the template at the window level, though it goes against the spirit of being able to restyle controls through markup alone; but it’s part of a general re-theming the user might well put the template into a ResourceDictionary, and now they have the same problems you did.

So why does WPF make it this difficult? Because WPF is trying to decouple the look of the control from the behaviour. The “look” — the template — shouldn’t have intimate knowledge of the behavioural code. The behavioural code shouldn’t be written with a specific user interface in mind. Communication between the code and the template should be loosely coupled.

One technique for this loose coupling is, of course, data binding. Elements in the template can bind to properties of the control. When the property changes, the element reflects that change, and vice versa. However, this doesn’t help us with the “month back” button: a button doesn’t have a property which changes in a useful way when the user clicks it, so we can’t use data binding to propagate this back to the calendar’s Date property. In the case of other “event handlers,” for example if the control exposes actions like Save or Update, there’s no property you could bind to even if the UI element did have something suitable.

For situations like this, WPF instead offers commands.

A command is represented by the ICommand interface, and usually implemented using the RoutedCommand class. Command objects don’t usually do much themselves: like DependencyProperty objects, they’re really just identifiers. Thus, a “month back” command doesn’t contain any logic about how to change the date on the calendar: it’s up to the calendar control (or anything else that could use a “month back” command) to decide how to handle the command. A control can consume built-in commands like Cut or Open, or can define and consume custom commands like MonthBack and MonthForward. Controls such as buttons and menu items can send these commands. And specifying which command a button sends is something we can do in pure markup, without any reference to code.

So in the calendar example, the calendar might define a custom command as follows:

public static readonly ICommand MonthBack = new RoutedCommand(...);

And the calendar control would then bind this command to an appropriate event handler:

public Calendar()
{
  CommandBindings.Add(new CommandBinding(MonthBack, MonthBack_Executed));
}

Now we — and anyone retemplating the control — can define the “month back” button to send this command:

<Button Command="{x:Static c:Calendar.MonthBack}">Back</Button>

This works whether in our theme resource dictionary, or an application resource dictionary, or a window-level template.

Commands, then, are the flip-side to bindable properties in defining the control’s “templateable” interface. Properties are the part of the interface that allow the template to represent and update the state of the control. Commands are the part of the interface that allow the template to perform actions on the control. Conversely, the set of properties and commands exposed by a control defines and limits what can be done with the control through its own user interface. If a control exposes a Save method, but no Save command, then applications can only perform the Save operation from code: neither the control author nor any other template designer can declaratively create a reusable template that gives access to the Save feature.

You’ll usually find that the commands interface falls naturally out of the default template: as you add UI elements to the default template, and wish that you could implement event handlers for them, that will be a clue that the control should provide a command for this situation. However, it’s also worth considering whether methods exposed by the control should also be commands, even if your default template doesn’t surface them. Another consideration is when to move part of the action definition out of the command and into a parameter: for example, should the Month/Week/Day Back/Forward calendar commands be combined into a ChangeDate command with a parameter, so that an enterprising template designer could implement a “fortnight back” or “year forward” button if it happened to be useful in their scenario? The answers depend on the individual control, of course; but if you think of commands as the API you expose to the template designer, you shouldn’t go too far wrong.

Tagged as WPF

Leave a Reply

Archives

Join our mailer

You should join our newsletter! Sent monthly:

Back to Top