Shawn Wildermuth

Author, Teacher, and Filmmaker
.NET Foundation Board Member

New SL4 Feature: Commanding


SilverlightFor those of you who have been living in the world of WPF, this post will be old-hat, but for the purely Silverlight folks I am hoping to help you change the way you add functionality to your applications. To do this, I will use Silverlight 4's new support for Commands.

Commands are a way to data bind to specific operations in your application. A command is any class that supports the ICommand interface. The ICommand interface supports three pieces of functionality:

public interface ICommand
  void Execute(object parameter);
  bool CanExecute(object parameter);
  event EventHandler CanExecuteChanged;

First and foremost, the interface supports an execute method that supports the actual operation that you want to execute. The next piece of functionality is a test to see if the command is valid.  Lastly, the command supports an event handler so that the command can notify users that the command needs to be re-evaluated to be sure the "CanExecute" is still valid.

The basic idea here is that if you expose commands and use data binding to attach them to your user interface (XAML). This avoid the normal procedure of writing button click handlers and figuring out how to execute some piece of functionality. In addition, the command can tell the controls whether the command is valid at a central point in time (this means no more messing with IsEnabled). 

In Silverlight 4, ButtonBase (and all its derived versions) and HyperlinkButton now support Command and CommandParameter to tie ICommand implementations.  For example, in XAML I can tie two different buttons to the SaveCommand that I have on some object i've bound to my UI:

<Border BorderBrush="LightGray"
  <StackPanel Orientation="Horizontal">
    <Button Command="{Binding SaveCommand}"
            CommandParameter="{Binding Person}"
            Content="Save" />
<StackPanel Grid.Row="1">
  <TextBlock FontWeight="Bold"
             Text="Person" />
  <TextBlock Text="Name" />
  <TextBox Text="{Binding Person.Name, Mode=TwoWay}" />
  <TextBlock Text="Occupation" />
  <TextBox Text="{Binding Person.Occupation, Mode=TwoWay}" />
  <Button Command="{Binding SaveCommand}"
          CommandParameter="{Binding Person}"
          Content="Save" />

By binding a SaveCommand to both of these buttons, whenever either of the buttons is pressed, the command will be executed.  But perhaps even more important is that as the command isn't valid (perhaps when the Person doesn't have changes), the buttons will be disabled.

To implement the command, I used Laurent Bugnion's MVVM Light framework.  He exposes a simple way to create commands with the RelayCommand<T> class.  When you create a RelayCommand<T> you simply specify lambda's for the Execute and optionally the CanExecute.  For example creating the SaveCommand is as simple as:

// Command for Saving
SaveCommand = new RelayCommand<Person>(p =>
    // Just accept the changes 
    // (though we'd really save the changes)

    // Cause the command to be reevaluated.
  p => p != null && p.HasChanges);

The first lambda represents the code that will run when the command is executed. The second lambda represents the code that is executed (returning a Boolean) to indicate whether the command is valid.  That means that if the person we're using in the command is either not null and has changes to save, then the buttons that this command is attached to should be valid. When the command is data bound, it evaluates the "CanExcute" lambda but to make this work the way we want, we'll need to cause that to be re-evaluated as necessary.  This is done with the RelayCommand<T>'s RaiseCanExecuteChanged.  This means that as our object is modified, we can simply re-evaluate the command's CanExecute.  Since the Person class implements INotifyPropertyChanged, I can use that behavior to test for CanExecute:

// If the person changes, 
// recheck the CanExecute part of the command
Person.PropertyChanged += 
  (s, e) => SaveCommand.RaiseCanExecuteChanged();

Go grab the code and play with it.  I think you will be compelled to use this in your next Silverlight application!