A smart MVVM command

A smart MVVM command

Note: If you are already familiar with MVVM and the command pattern, you can skip the intro and jump directly to the interesting part!

The Model-View-ViewModel pattern became very popular in the last years.

If you call yourself a .NET client developer and are not already familiar with this pattern, find a good tutorial and start learning it. Now!

While MVVM is “just” a software design pattern, there are a couple of popular MVVM frameworks out there, which reduce the effort of writing all the necessary code. One of them is the MVVM Light Toolkit from my MVP fellow Laurent Bugnion.

The toolkit tries to keep things simple, but comes with a lot of MVVM-goodness, like a base ViewModel, a simple IoC container, a messenger for loosely coupled communication and a RelayCommand, which is a straightforward implementation of the .NET ICommand interface. Other frameworks have the exact same thing but use a different name for this class, like DelegateCommand.

When following the MVVM pattern, you typically bind a property of a control in your View to a property in your ViewModel, thus avoiding writing spaghetti code in your code behind. The ViewModel is usually an implementation of the INotifyPropertyChange interface (INPC), so that we can notify the View by raising the PropertyChanged event, of any changes in the underlying ViewModel

These are just the basics of every MVVM approach and typically, it will look like this piece of code:

View:

    <TextBox Text="{Binding Path=Name, Mode=TwoWay}" />

ViewModel:

public class SampleViewModel : ViewModelBase
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (value != _name)
            {
                _name = value;
                RaisePropertyChanged("Name");
            }
        }
    }
}

What, if we want to react to some interaction from the user, like clicking on a button? Instead of writing an event-handler in code behind, we can leverage the command pattern! Every XAML control, which derives from ButtonBase has a property Command, which in turn can be bound to an ICommand object. This interface defines just two methods and one event.

If the button is clicked, the Execute method of the ICommand will be called.

The MVVM Light Toolkit comes with an implementation of the ICommand – called RelayCommand – that awaits an Action for the Execute method in the constructor.

Usage in XAML:

<Button Command="{Binding Path=SaveCommand}" Content="Save" />

Declaration in ViewModel:

public class SampleViewModel : ViewModelBase
{
    public ICommand SaveCommand { get; private set; }

    public SampleViewModel()
    {
        SaveCommand = new RelayCommand(Save);
    }

    private void Save()
    {
        // Do some stuff
    }

}

And there is even more: if the CanExecute method of the ICommand returns false, the button itself will be greyed out and is not clickable! Isn’t that cool? Without writing a single line of code to enable or disable the button control!

But wait – what if the CanExecute return-value might change? How will the bound button be notified, that he needs to refresh its enabling state? Well, just by raising the CanExecuteChanged event of the ICommand!

You can pass a second parameter to the constructor of the RelayCommand, which contains the function for the CanExecute method. The RelayCommand has a method RaiseCanExecuteChanged that can be called from outside to fire the CanExecuteChanged event.

public class SampleViewModel : ViewModelBase
{
    public RelayCommand SaveCommand { get; private set; }

    public SampleViewModel()
    {
        SaveCommand = new RelayCommand(Save, CanSave);
    }

    private bool CanSave()
    {
        return !string.IsNullOrEmpty(Name);
    }

    private void Save()
    {
        // Do some stuff
    }

    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (value != _name)
            {
                _name = value;
                RaisePropertyChanged("Name");
                SaveCommand.RaiseCanExecuteChanged();
            }
        }
    }

}

In the sample above, the button is grayed out, if the name in the textbox is empty. As soon, as the user types in a value, the button becomes available.

Instead of explicit methods, you can of course also write anonymous methods using Lambda expressions, of course.

    public SampleViewModel()
    {
        SaveCommand = new RelayCommand(() => { /* do some stuff; */ }, 
                                       () => !string.IsNullOrEmpty(Name));
    }

If you used the Command pattern before, there was probably nothing new for you so far in this article. So now comes the interesting part… :)

The annoying thing so far, is that you have to call the RaiseCanExecuteChanged method manually. In this simple example, it is just one call – so no big deal at all! However, it can be more complicated depending on your logic.

To simplify this step, I derived from Laurent’s RelayCommand and added some code to fire the CanExecuteChange event automatically, whenever a property, that has influence to the CanExecute return-value, changes itself. Because the execution of this RelayCommand depends on those properties, I named it DependentRelayCommand.

Here is the implementation, which I already published to NuGet:

public class DependentRelayCommand : RelayCommand
{

    private readonly List<string> _dependentPropertyNames;

    public DependentRelayCommand(Action execute, Func<bool> canExecute,
                                 INotifyPropertyChanged target,
                                 params string[] dependentPropertyNames)
        : base(execute, canExecute)
    {
        _dependentPropertyNames = new List<string>(dependentPropertyNames);

        target.PropertyChanged += TargetPropertyChanged;
    }

    public DependentRelayCommand(Action execute, Func<bool> canExecute,
                                 INotifyPropertyChanged target,
                                 params Expression<Func<object>>[] dependentPropertyExpressions)
        : base(execute, canExecute)
    {
        _dependentPropertyNames = new List<string>();
        foreach (var body in dependentPropertyExpressions.Select(expression => expression.Body))
        {
            var expression = body as MemberExpression;
            if (expression != null)
            {
                _dependentPropertyNames.Add(expression.Member.Name);
            }
            else
            {
                var unaryExpression = body as UnaryExpression;
                if (unaryExpression != null)
                {
                    _dependentPropertyNames.Add(((MemberExpression)unaryExpression.Operand).Member.Name);
                }
            }
        }

        target.PropertyChanged += TargetPropertyChanged;

    }

    private void TargetPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (_dependentPropertyNames.Contains(e.PropertyName))
        {
            RaiseCanExecuteChanged();
        }
    }

}

Just add the package using NuGet to your project.

The usage of this class is rather simple:

Pass an action for the Execute method and a function for the CanExecute algorithm like usual, followed by a reference to the ViewModel itself and add a list of strings for all dependent properties that might influence the outcome of the CanExecute method.

    SaveCommand = new DependentRelayCommand(() => { /* do some stuff; */ }, 
                                            () => !string.IsNullOrEmpty(Name),
                                            this, "Name");

There is also an overload that takes lamda expressions instead of strings, which makes the statement less error-prone.

    SaveCommand = new DependentRelayCommand(() => { /* do some stuff; */ }, 
                                            () => !string.IsNullOrEmpty(Name),
                                            this, () => Name);

Of course, the RelayCommand can be dependent on more than one properties:

    SaveCommand = new DependentRelayCommand(() => { /* do some stuff; */ }, 
                                            () => !string.IsNullOrEmpty(Name),
                                            this, 
                                            () => FirstName, () => LastName);

This becomes extremely useful in combination with PropertyChanged.Fody.

Fody is a tool developed by Simon Cropp that manipulates IL (Intermediate Language) right after the build process. After adding Fody and the PropertyChanged add-in to your project using NuGet, it injects the RaisePropertyChanged call for all properties in a class that implements INPC (which every ViewModel does).

So if you have an automatic property in your ViewModel

    public string Name { get; set; }

the following gets compiled:

private string name;

public string Name
{
    get { return name; }
    set
    {
        if (value != name)
        {
            name = value;
            RaisePropertyChanged("Name");
        }
    }
}

Unfortunately, there is no explicit opt-in attribute that makes the usage of Fody reproducible for other developers. Therefore, I strongly recommend adding a comment to document where the magic happens.

Anyway, Fody is extremely cool stuff and I recommend to give it a try. It was also Simon, who pointed me into the direction of extending the RelayCommand, when I was looking for a solution to couple a command to some properties.

So finally, thanks to the DependentRelayCommand and PropertyChanged.Fody, the ViewModel becomes as simple and readable as this:

using GalaSoft.MvvmLight;
using Mutzl.MvvmLight;

public class SampleViewModel : ViewModelBase
{
    //INPC injected using Fody

    public SampleViewModel()
    {
        SaveCommand = new DependentRelayCommand(() => { /* do some stuff; */ }, 
                                                () => !string.IsNullOrEmpty(Name), 
                                                this, () => Name);
    }

    public string Name { get; set; }
    public ICommand SaveCommand { get; private set; }
}

 

Focusing on business logic instead of writing verbose, repetitive code – cool, isn’t it?!

 

.NET Architecture Development English

3 comments

  1. Matthias says:

    Daniel Moore as gone even further in WpfIgniter whith his ExpressionCommand.
    He parses the canExecute which MUST be a lambda and detect automatically all the properties which are INotifyPropertyChanged, INotifyCollectionChanged or dependency properties and watch them to raise the CanExecuteChanged accordingly.
    It even works when the ICommand parameter changes.

    Take a look :
    https://github.com/danielmoore/WpfIgniter

Leave a Comment

Your email address will not be published. Required fields are marked *