Adventures in MVVM – Commands in Silverlight

Adventures in MVVM Shout it kick it on DotNetKicks.com

EDIT:

One of the things that I enjoy most about working with MVVM in Silverlight is how new it is.  When I say “new”, I mean that Silverlight doesn’t support the pattern very well out of the box, so the development community needs to step up and solve these problems.  Some solutions are better than others.  In this article, I solved a problem the best way I knew how.  I urge you to read Ward Bell’s comments to this article, and my responses.  After reflecting on it more, I concede that this is not the best way to implement a lightweight bindable command in SIlverlight.

Instead, I should have just read the Prism 2 source code to see how they implemented commands via attached behaviors.  I would have learned that that I learned attached behaviors wrong in the first place.  In Julian Dominguez’s blog post on the topic, he walks you through the thought process for attaching commands via behaviors.  Although this is not the final code that made it into Prism, it is very close.  I recommend reading it.

That being said, I will keep my original text in place.  I think that the thought process for how I got there is very useful for learning… at least I find it useful.  Then, be sure to read the comments, visit Julian’s blog and browse the Prism (CAL) source code.

One of the most important aspects of implementing the MVVM pattern in WPF and SIlverlight is the ability for the UI layer to bind directly to commands in the ViewModel.  The only problem with this:  commands were never implemented in Silverlight.  Even though I (and many others) have ranted about this, our voice has not been heard.  Even with the release of the Silverlight 3 beta, it seems as if we are still pining for commanding in Silverlight.

Many libraries have implemented commands in Silverlight, usually with some sort of static lookup table, mapping buttons to commands.  They include Prism, Caliburn, SilverlightFX and the MVVM toolkit.  It can feel like overkill to bring in these libraries just to get commanding.  There are plenty of good reasons to use these libraries – don’t get me wrong – but if you are just looking for bindable commands, there is an easier way.

This article will walk you through the process of creating a button with command properties.  This technique can be translated easily to any other control in order to achieve bindable commanding in Silverlight.

ICommand

The ICommand interface was the only thing that was included from the WPF commanding infrastructure within Silverlight.  The interface is extremely simple:

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

The requirements of any control that deals with ICommand are:

  1. Call Execute() when a trigger is hit
  2. Only call Execute() if CanExecute() returns true
  3. Allow a bindable parameter to be passed into Execute() and CanExecute()
  4. Disable the control when CanExecute() is false
  5. Refresh the enable/disable state of the control when the CanExecuteChanged event is raised

Implementing CommandButton

Lets start with requirements 1 and 2:

public class CommandButton : Button
{
    public CommandButton()
    {
        Click += (sender, e) =>
        {
            if (Command != null && Command.CanExecute(null))
                Command.Execute(null);
        };
    }

    public static DependencyProperty CommandProperty =
        DependencyProperty.Register("Command",
                                    typeof(ICommand), typeof(CommandButton),
                                    new PropertyMetadata(null));

    public ICommand Command
    {
        get { return GetValue(CommandProperty) as ICommand; }
        set { SetValue(CommandProperty, value); }
    }
}

With this, you can bind a command in the ViewModel to the view:

public class ViewModel : INotifyPropertyChanged
{
    public ViewModel()
    {
        MyCommand = new DelegateCommand<object>(DoSomething);
    }

    private void DoSomething(object obj)
    {
        // Do what you want
    }

    public ICommand MyCommand { get; private set; }

   // The rest of your ViewModel
}

This XAML creates a CommandButton in place of a Button:

<local:CommandButton Content="Click Me" Command="{Binding MyCommand}" />

Adding Parameters

Implementing feature 3 is trivial. Add the CommandParameter property and pass it in to Execute() and CanExecute()

public class CommandButton : Button
{
    public CommandButton()
    {
        Click += (sender, e) =>
        {
            if (Command != null && Command.CanExecute(CommandParameter))
                Command.Execute(CommandParameter);
        };
    }

    // Everything else from initial Implementation

    public static DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter",
                                    typeof(object), typeof(CommandButton),
                                    new PropertyMetadata(null));

    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }
}

With that, you can add parameters to the XAML:

<local:CommandButton Content="Click Me" Command="{Binding MyCommand}" CommandParameter="MyParameter" />

Hooking IsEnabled to CanExecute()

Things get a bit more complicated when implementing requirements 4 and 5, but it is still pretty straight-forward.  I start by registering an event handler for when the Command property changes (CommandChanged).  This event handler hooks the CanExecuteChanged event and handles the event by setting the IsEnabled flag to the value of CanExecute().  It then proceeds to initialize the value of IsEnabled since we know the answer at this time.

The final class:

public class CommandButton : Button
{
    public CommandButton()
    {
        Click += (sender, e) =>
        {
            if (Command != null && Command.CanExecute(CommandParameter))
                Command.Execute(CommandParameter);
        };
    }

    public static DependencyProperty CommandProperty =
        DependencyProperty.Register("Command",
                                    typeof(ICommand), typeof(CommandButton),
                                    new PropertyMetadata(null, CommandChanged));

    private static void CommandChanged(DependencyObject source, DependencyPropertyChangedEventArgs args)
    {
        var button = source as CommandButton;
        if (button == null) return;

        button.RegisterCommand(args.OldValue as ICommand, args.NewValue as ICommand);
    }

    private void RegisterCommand(ICommand oldCommand, ICommand newCommand)
    {
        if (oldCommand != null)
            oldCommand.CanExecuteChanged -= HandleCanExecuteChanged;

        if (newCommand != null)
            newCommand.CanExecuteChanged += HandleCanExecuteChanged;

        HandleCanExecuteChanged(newCommand, EventArgs.Empty);
    }

    private void HandleCanExecuteChanged(object sender, EventArgs args)
    {
        if (Command != null)
            IsEnabled = Command.CanExecute(CommandParameter);
    }

    public ICommand Command
    {
        get { return GetValue(CommandProperty) as ICommand; }
        set { SetValue(CommandProperty, value); }
    }

    public static DependencyProperty CommandParameterProperty =
        DependencyProperty.Register("CommandParameter",
                                    typeof(object), typeof(CommandButton),
                                    new PropertyMetadata(null));

    public object CommandParameter
    {
        get { return GetValue(CommandParameterProperty); }
        set { SetValue(CommandParameterProperty, value); }
    }
}

Summary

This method for hooking commands to buttons has one drawback in my opinion:  It requires you to put a CommandButton in your XAML instead of a vanilla Button.  This, of course, means that other controls that inherit from Button such as Checkbox and RadioButton do not get this behavior (you have to implement this pattern for them).  Still, this approach makes it very easy to add commands to any control you wish; even in WPF.  In a future post, I will discuss a similar approach for binding a command to a ComboBox (or ListBox) selection changed event.

6 Responses to “Adventures in MVVM – Commands in Silverlight”

  1. Chris says:

    Great post, thanks, but where can I find the DelegateCommand class?

  2. admin says:

    The DelegateCommand is a very simple implementation of ICommand that wraps two delegates: Execute and CanExecute. They can be found all over the internet, but one implementation can be found in my “ViewModelSupport” project:
    http://viewmodelsupport.codeplex.com/SourceControl/changeset/view/48146#864924

  3. He says:

    Is there possible to bind in MVVM an event to a command?
    Let’s say I want to have a view-model for a TabControl and I want a command for SelectionChanged event. So, is there possible to have <TabControl SelectionChanged=”{Binding TabSelectionCommand}”>….</TabControl> ? If it is so, how can it be implemented?

    Thank you!

  4. admin says:

    Yes, by using the attached behavior property, you can do this. I discuss it further in this post:
    http://houseofbilz.com/archives/2009/08/27/adventures-in-mvvm-binding-commands-to-any-event/

  5. swapnil says:

    Hi,
    i have a tab control and some fieled in it.if user inputs some changes in the fields and then click on some other menu outside tabcontrol then it must show a message box regarding saving of data if he presses “yes” then data will be saved else the control will jump on the menu he has selected.
    Can you help me finding the solution.
    i have to implement it using MVVM in silverlight 4.0

  6. Can you explain me why commanding is not working for checkbox and radio button?

Leave a Reply