Using RelayCommands in Silverlight 3 and WPF

Like most of us have found out the hard way, Silverlight 3 and Windows Presentation Foundation 3.5 are two quite similar beasts, but not totally the same. Silverlight is often presented as a subset of WPF (in fact, some features are available in Silverlight but not yet in WPF; generally speaking, though, it is true that Silverlight has less features than its big sister WPF) (yeah WPF is a girl, don’t tell me you didn’t know ;))

No Commands in Silverlight 3

One of the missing parts is Commanding. Commands in WPF are used as a kind of “weak events”, i.e. they can be used to execute an action when a control is actuated (just like an event), but without the tight coupling that an event brings with it. You can specify a Command on an object, and bind this command to a control in a completely different part of your application. You can have one person write the Command (typically a developer), and a completely other person (typically a designer) use the Command in the user interface. More interestingly, the designer can change the look and feel, for example replacing a series of Buttons by a Menu and MenuItems, and the developer doesn’t need to change anything to the code.

In Silverlight, parts of the “plumbing” are available already (the ICommand interface) but most of it is missing. This led some very clever people to implement Commands in Silverlight, to replace the missing pieces by a custom implementation. In Prism, the command implementation is called DelegateCommand. In my MVVM Light Toolkit, I use a command that my good friend, colleague at IdentityMine and fellow WPF Disciple Josh Smith created, called the RelayCommand.

Now, the beautiful part is that RelayCommands (and DelegateCommands too) work wonders in Silverlight, but also in WPF. RelayCommands are making your life much easier in WPF too. No need to implement complicated code to bind the control to your method, just use a RelayCommand to do that. And since the implementation is (almost) the same in Silverlight too, you can share code between Silverlight and WPF.

The Execute Method

When the control to which a Command is bound is actuated by the user, the Execute method is called. This method is specified by the ICommand interface, and every ICommand implementation must offer it (and of course RelayCommand does).

The Execute method is passed to the RelayCommand’s constructor in form of an Action (or Action<T> if the command has a parameter, in which case you must use the RelayCommand<T> class).

Note: An Action is a method  (returning void) that can be passed to another object, for example as a parameter. It is something like a method pointer. If the method that the Action represents has a parameter, you must use Action<T> where T is the parameter’s type. There are also additional Action implementations with more than one parameters, but RelayCommand can only have 0 or 1 parameter.

The code to create a RelayCommand is:

public RelayCommand MyCommand
{
    get;
    private set;
}

public MainViewModel()
{
    MyCommand = new RelayCommand(
        () => DoSomething());
}

Note: The syntax () => DoSomething() is called a lambda expression, and is the shortest way to define and pass an Action to an object. In the sample here, the method DoSomething will be called without any parameter when the Command is executed.

CanExecute and CanExecuteChanged

Apart from the Execute method that the ICommand interface specifies, there is an additional method and an event that every ICommand (including, of course, RelayCommand) must offer.

  • CanExecute: This method should return true if the command can be executed. When the Command is used on a control, the control will be disabled automatically when CanExecute returns false.
  • CanExecuteChanged: This event should be raised when the CanExecute method must be “requeried” (when the value of CanExecute might have changed, and should be re-evaluated).

The CanExecute method can be passed to the RelayCommand constructor as a Func<bool> (or a Func<T, bool> if the command has a parameter).

Note: A Func is exactly like an Action, except that it has a return type. In the case of RelayCommands, the return type is bool: True if the command can be executed, false otherwise.

To create a command with an Execute and a CanExecute methods, you can do:

public RelayCommand MyCommand
{
    get;
    private set;
}

public MainViewModel()
{
    MyCommand = new RelayCommand(
        () => DoSomething(),
        () => return MyValue);
}

In the code above, if MyValue is true, the command can be executed; if it is false, the command cannot be executed (and controls using that command are disabled). This can depend on multiple parameters such as the state of the application, the time of the day, the weather outside, etc.

Note: When using lambda expressions, if the expression is just “return something”, the “return” keyword can be omitted. So the code becomes:

public RelayCommand MyCommand
{
    get;
    private set;
}

public MainViewModel()
{
    MyCommand = new RelayCommand(
        () => DoSomething(),
        () => MyValue);
}

The CommandManager

RelayCommands are available for WPF and Silverlight. However, one piece is missing in Silverlight: Windows Presentation Foundation has a class that is watching your UI, and that polls the commands’ state when something happens (for example when the user clicks a button, checks a checkbox, open a menu, etc…). This class is called the CommandManager and it doesn’t exist in Silverlight. Not at all. Nada.

So when a user actions the UI in WPF, this triggers the CommandManager to requery all the commands. In Silverlight you have to do the work yourself. This is why the RelayCommand class has a method called RaiseCanExecuteChanged. This method forces the control that uses the command to check again if the command may be executed or not. And if the command may not be executed, the control is disabled.

In WPF

Strictly speaking, in WPF, and if your command is bound to a control that is watched by the CommandManager, you shouldn’t have to raise the CanExecuteChanged event yourself. You can let the CommandManager handle the situation. That said, external events might also change the state of the UI. Let’s imagine that the UI should be enabled from 9AM to 5PM, and then disabled for the night. The user is not triggering the UI, so the code should request (politely) that the CommandManager requeries the state of the commands. This is done by calling the method InvalidateRequerySuggested on the CommandManager. And as you guessed, the method RaiseCanExecuteChanged of the RelayCommand class does just that.

And in Silverlight

However, since Silverlight doesn’t have the CommandManager, a little more manual work is involved. When the state of your object changes in a way that affects the UI, you must RaiseCanExecuteChanged on the corresponding commands. In Silverlight, this will raise the CanExecuteChanged event, thus forcing the bound controls to call CanExecute. In WPF,  like we just saw, this will call InvalidateRequerySuggested on the CommandManager.

Show me the code

I created a small demo to illustrate this in Silverlight and in WPF, using the MVVM Light Toolkit. Most of the code is shared between WPF and Silverlight. The source code for the application is available as usual (WPF 3.5 SP1 and Silverlight 3).

The demo exposes one command, created in the MainViewModel class:

public RelayCommand IncreaseCounterCommand
{
    get;
    private set;
}

public MainViewModel()
{
    IncreaseCounterCommand = new RelayCommand(
        () => Counter++,
        () => CanIncrement);
}

The Counter and CanIncrement properties are raising the PropertyChanged event when they are modified, thus data bindings that refer to these two properties will be updated whenever the properties are modified. What the code above means is: When the IncreaseCounterCommand is executed, check the CanIncrement property. If it is true, increment the Counter property.

2009092601

Figure 1: WPF demo application, Enabled and Disabled

Disabling the button

We have a manual way to enable/disable the “increment” button: If you click on the “Enabled” text, it will turn into “Disabled”. This text (it’s a ToggleButton) is bound to the CanIncrement property directly. So in fact, when the text is “Disabled”, the CanIncrement property is set to false.

Of course, we want the controls using this command to be automatically disabled/enabled when the CanIncrement property changed. In WPF, nothing needs to be done, because the CommandManager will automatically requery the state of the commands when the user clicks on the UI. In fact the state of the commands is requeried very, very often during the course of the application. Even just moving the mouse on the UI causes the CommandManager to requery the state of commands bound to visible UI element.

In Silverlight however, no CommandManager, so we need to raise the CanExecuteChanged event on the command manually. Note that raising the CanExecuteChanged event can be useful in WPF too if you change the CanIncrement property in the code, for example through a timer. So, in fact, we will just call IncreaseCounterCommand.RaiseCanExecuteChanged() in Silverlight and in WPF too when CanIncrement changes:

public bool CanIncrement
{
    get
    {
        return _canIncrement;
    }

    set
    {
        if (_canIncrement == value)
        {
            return;
        }

        _canIncrement = value;

        // Update bindings, no broadcast
        RaisePropertyChanged(CanIncrementPropertyName);

        IncreaseCounterCommand.RaiseCanExecuteChanged();
    }
}

Just a warning: In V1 of the MVVM Light Toolkit, the method RaiseCanExecuteChanged was taking a boolean parameter. This boolean was actually not very useful, and has been removed in V2. Calling the old method still works in V2, but the parameter has no effect, and the method has been marked Obsolete. It will be removed in a future version of the toolkit.

This sample uses V2 beta of the MVVM Light Toolkit.

Silverlight/WPF Compatibility

Much of the code is shared between Silverlight and WPF in this small demo.

  • The ViewModel code is completely shared. The source code files are physically into the WPF project, and added as links into the Silverlight project. Implement once, compile twice.
    To add a source code file as a link, choose “Add existing item” in Visual Studio. Then, select a file. Instead of clicking on Add, click on the arrow on the right of the Add button, and select “Add as a link”.
  • The resources defining the look&feel of the application are placed into the file Skins\MainSkin.xaml. The code n this file is 100% the same in WPF and in Silverlight. To reuse the Silverlight control templates in WPF too, I use the WPF Toolkit’s implementation of the Visual State Manager.
  • Finally, the XAML code in the main UI element (MainWindow.xaml in WPF, and Page.xaml in Silverlight) is almost the same, except for one little difference: Since Silverlight doesn’t support commands out of the box, we rely on an attached behaviour to bind the RelayCommand to the Button:
<Button cmd:ButtonBaseExtensions.Command="{Binding IncreaseCounterCommand}"
        Grid.Row="2"
        Style="{StaticResource IncrementButtonStyle}"
        Content="Increment" />

Conclusion

Commands in WPF are quite a complex topic, and I must admit that I didn’t completely understand them before I discovered the RelayCommand class. I totally fell in love with this fantastic helper. Josh Smith has been fantastic in allowing me to re-use his code in the MVVM Light Toolkit. I hope that this article will help you understand the power and convenience of RelayCommands. Happy coding!

 

Print | posted on Saturday, September 26, 2009 7:15 AM

Feedback

# re: Using RelayCommands in Silverlight and WPF

left by Josh at 9/26/2009 8:33 PM Gravatar
This feature is already implemented in Prism/CAG almost identically:

http://prism.codeplex.com/

In V2 which released in February. It also contains a DelegateCommand which is similar to RelayCommand but based on John Gossman's original from Blend.

# re: Using RelayCommands in Silverlight and WPF

left by Laurent Bugnion at 9/26/2009 10:00 PM Gravatar
Hi Josh,

You are right, and this is not the only thing that is similar to what Prism is offering. As I mentioned on your own blog, I also have a Messenger which is an implementation of the Event Aggregator.

The goal of the toolkit is to offer an alternative, directly targeted at MVVM (while Prism is targeted at composite applications, with some of the elements that can be used for MVVM), and in a form that works best with Expression Blend. This is a lighter framework than Prism, which should be easier to start with, and which integrates better in Blend for the design time experience.

Glenn Block and I argued a LOT about reusing pieces of Prism, packaging them differently, or implementing something slightly different. I chose the second approach mostly because this gives me more flexibility in pushing changes that the community requests. This is already what happened with the Messenger V2 (I will post the beta later today), and I expect the same to happen to other pieces too). I think that Glenn (who was an alpha tester) agrees with me that the toolkit is a very light and flexible approach to MVVM, and easy to integrate in existing applications (including those built with IoC etc...).

Thanks for your feedback, and don't hesitate to give more if you have suggestions.

Cheers,
Laurent

# re: Using RelayCommands in Silverlight and WPF

left by Raghuraman at 10/4/2009 11:02 AM Gravatar
Great Work Guys !!!

This will pull down the learning curve for wannabe MVVM implementations in SL.

Keep it up.

# re: Using RelayCommands in Silverlight and WPF

left by ETFairfax at 10/19/2009 8:20 PM Gravatar
Hi,

I'm brand new to all of this Silverlight/MVVM stuff, so please be gentle!!

If I have a user control which hass two buttons and include that on my page. Is it possible to add a command to the Click event of each of the two buttons? Something like....

<uc:MyUserControl SaveEvent="{Binding Path=SaveCommand}" CancelEvent="{Binding Path=CancelCommand}" />

I've got commands working with single buttons i.e...

<Button Content="Delete" cmd:ButtonBaseExtensions.Command="{Binding Path=DeleteCommand}" />

But can't work out what i need to do to make this work on a user control.

Does that make sense??

Cheers,

ETFairfax.

# re: Using RelayCommands in Silverlight and WPF

left by Laurent at 10/19/2009 9:50 PM Gravatar
Hi,

It does make a lot of sense. Historically, this restriction comes from WPF, where only some controls (all ButtonBase, MenuItems...) implement ICommandSource, which specify the Command property. Only these controls can have a Command, and the command is only activated on Click event.

Thankfully, there are ways around that, and I am working on a new version of the MVVM Light toolkit that will include a "Event to Command" binder. It will be strongly inspired from works by the Expression Blend team as well as Orktane's nRoute toolkit, so in the mean time I encourage you to check these.

If you prefer to stick with the MVVM Light toolkit, one transient measure you can take is to handle the event in the code behind, and execute the command from there. This way, when the EventToCommand binder is available, you can just remove the code behind and do that in the XAML instead.

http://expressionblend.codeplex.com/

http://www.orktane.com/Blog/post/2009/10/10/Introducing-nRouteToolkit-for-Silverlight-(Part-II).aspx

Cheers,
Laurent

# re: Using RelayCommands in Silverlight and WPF

left by ETFairfax at 10/20/2009 9:15 PM Gravatar
Nice one Laurent. I'll have a look at those resources.

I look forward to the next release of the toolkit.

Thanks,
ETFairfax

# re: Using RelayCommands in Silverlight 3 and WPF

left by Werner at 1/19/2010 11:12 PM Gravatar
Hi!

Any news on that EventToCommand binder?

Really really would like that. PRISM's way of getting custom events to work is way way way to involved for my taste.

;)

# re: Using RelayCommands in Silverlight 3 and WPF

left by Laurent at 1/19/2010 11:40 PM Gravatar
Hey Werner,

Yes, EventToCommand is available in the latest release of the toolkit. See
http://blog.galasoft.ch/archive/2009/11/13/bug-correction-in-messenger-and-new-feature-in-eventtocommand-mvvm.aspx

and also

http://blog.galasoft.ch/archive/2009/12/17/silverlight-4-dragampdrop-with-eventtocommand.aspx

Hope this helps!
Laurent

# re: Using RelayCommands in Silverlight 3 and WPF

left by Werner at 1/20/2010 12:22 AM Gravatar
That is excellent news. I have found those extentions hiding in "extras".

I have another question. For some bizarre reason I cannot find the ButtonBaseExtensions part of the Commanding inside your MVVM.Lite library.

What am I misssing? If I do a keyword search throughout MVVM.Lite I find now references to that name. I am under the understanding that it must come from MVVM.Lite?

What am I not getting here?

# re: Using RelayCommands in Silverlight 3 and WPF

left by Laurent at 1/20/2010 12:40 AM Gravatar
Hi,

ButtonBaseExtension is only needed (and available) in the Silverlight 3 version of the toolkit. In the WPF 3.5, WPF 4 and Silverligh 4 versions, you should use the built-in property Command which is available on all ButtonBase instances.

In the Silverlight 3 version, this class is available in the GalaSoft.Mvvm.Command namespace.

Make sure that you have the correct version of the DLLs if you need this class!

Cheers,
Laurent

# re: Using RelayCommands in Silverlight 3 and WPF

left by Werner at 1/20/2010 12:46 AM Gravatar
Aah!

I did not know this.

I am indeed working with SL4. I was aware that SL4 has some commanding support, but also just on buttons. It's your EventToCommand binder I was aftet so I did not check was already available in SL4

The commanding is working now.

Thanks allot!

Werner

PS - Awesome toolkit I like it

# re: Using RelayCommands in Silverlight 3 and WPF

left by Dave at 2/25/2010 4:55 AM Gravatar
Great article! I am planning on using your toolkit, but am currently looking into problems where my GUI doesn't actually update as expected; when properties that govern whether my command CanExecute or not change, the GUI doesn't update unless I actually click. Just moving the mouse isn't enough. I am thinking about calling InvalidateRequerySuggested in a Timer for now, until I can implement your toolkit. What are your thoughts on that? Oh, and I'm using WPF with .NET 3.5 SP1
Post A Comment
Title:
Name:
Email:
Website:
Comment:
Verification: