Ranting and raving about anything I feel like complaining about.

New SL4 Feature: Commanding

Url: http://wildermuth.com/downloads/funwithcommandi...

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"
        BorderThickness="1">
  <StackPanel Orientation="Horizontal">
    <TextBlock>Tools:</TextBlock>
    <Button Command="{Binding SaveCommand}"
            CommandParameter="{Binding Person}"
            Content="Save" />
  </StackPanel>
</Border>
<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}"
          Width="100"
          HorizontalAlignment="Right"
          Content="Save" />
</StackPanel>

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)
    p.AcceptChanges();

    // Cause the command to be reevaluated.
    SaveCommand.RaiseCanExecuteChanged();
  },
  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!

http://wildermuth.com/downloads/funwithcommanding.zip

 

 
 

Comments

Gravatar Great post! Thanks for sharing.
Gravatar Shawn,
thanks for the code; one problem though; I got:

The "ValidateXaml" task failed unexpectedly.
System.IO.FileLoadException: Could not load file or assembly 'file:///C:\Users\Marc\Documents\Visual Studio 2008\Projects\FunWithCommanding\externals\GalaSoft.MvvmLight.dll' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)

When I tried to build. There was a link that talks to the issue further in the error text, but it was not very helpful. What was helpful was Brian Leach's blog that solved the problem for me.
http://mvdeveloper.blogspot.com/2009/12/silverlight-4-cas-restrictions.html

Hope this is useful,
Marc Ziss
Gravatar Shawn,
thanks for the code; one problem though; I got:

The "ValidateXaml" task failed unexpectedly.
System.IO.FileLoadException: Could not load file or assembly 'file:///C:\Users\Marc\Documents\Visual Studio 2008\Projects\FunWithCommanding\externals\GalaSoft.MvvmLight.dll' or one of its dependencies. Operation is not supported. (Exception from HRESULT: 0x80131515)

When I tried to build. There was a link that talks to the issue further in the error text, but it was not very helpful. What was helpful was Brian Leach's blog that solved the problem for me.
http://mvdeveloper.blogspot.com/2009/12/silverlight-4-cas-restrictions.html

Hope this is useful,
Marc Ziss
Gravatar Great explanation as always! However I have a simple WCF RIA MVVM app that reads a few categories from good old Northwind and displays them in a DataGrid. My viewmodel is using Laurent Bugnion's MVVM Light and looks like:

public class CatViewModel : ViewModelBase
{
public RelayCommand<Category> SaveCommand { get; set; }
public NWDomainContext dc = null;
public IEnumerable Cats { get; set; }
public CatViewModel()
{
dc = new NWDomainContext();
LoadOperation loadop = dc.Load<Category>(dc.GetCategoriesQuery());
loadop.Completed += new EventHandler(loadop_Completed);
Cats = dc.Categories;
SaveCommand = new RelayCommand<Category>(c =>
{
//will save something later
SaveCommand.RaiseCanExecuteChanged();
},
c => c != null && c.HasChanges);
}
void loadop_Completed(object sender, EventArgs e)
{
// WAS thinking this would help enable my save button
foreach (Category cat in dc.Categories)
cat.PropertyChanged += (s, e1) => SaveCommand.RaiseCanExecuteChanged();
}
Now my code populates my DatGrid and I have a Save button whose XAML looks like
<Button Content="Save" Height="23" Name="button1" Width="75" Command="{Binding SaveCommand}" CommandParameter="{Binding Category}"></Button>
My goal is to have the Save button only enabled if changes have been made to the Category objects and that is why I set the PropertyChanged event on Category to raise the CanExecuteChanged. Indeed this works and the line of code c => c != null && c.HasChanges); is executed but alas c is always null.Was wondering what I was missing or if this approach made sense. I know it works in your case where u "new: up the objects so I must be missing the obvious here.

Thanks

Gravatar Has someone written up the differences between WPF and Silverlight in regard to the gap libraries like Prism, Caliburn, MVVM Light Toolkit are trying to bridge.

I would prefer not to use third party libraries to fully implement MVVM in Silverlight, but it appears to be a necessary evil.

Basically, I was hoping someone blogged/documented a comparison so I could make an informed decision on what is missing and whether or not I want to code the difference or use the available libraries.

I guess a follow-up question should be if you are going to use one, which is the one you would choose? I would guess MVVM Light Toolkit, but then again you did post this in February and it is now June.

As we all know this technology is changing fast as so are the decisions we make to developing in Silverlight need to keep pace. :)

Thanks, Jeff
Gravatar In Silverlight 4 (not sure about previous versions), you can use InvokeCommandAction (System.Windows.Interactivity) in xaml to make use of command binding to events on controls other than ButtonBase.

I use it all the time and it works great.

Add a Comment

*
*
*