MVVM – Commands

Make it So, Number One

The ICommand interface contains three elements

  • Execute – an action to invoke when the command is triggered
  • CanExecute – an optional function to check whether the command is currently enabled
  • CanExecuteChanged – an event that is fired to tell anything bound to the command to re-evaluate the CanExecute function

I provided a very basic ICommand implementation in the initial version of the StaffManager demo application.

public class Command: ICommand
{
    private readonly Action _execute;

    public Command(Action execute)
    {
        _execute = execute;
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _execute?.Invoke();
    }

    public event EventHandler CanExecuteChanged;
}

In this post, I’m going to expand on this to show the full power of commands within a WPF application. I’ve added an upgraded ICommand implementation to the library, based on MVVMLight’s RelayCommand structure.

First a common base class that adds a method for manually triggering the CanExecuteChanged event …

public abstract class perCommandBase : ICommand
{
    // ICommand members
    public abstract bool CanExecute(object parameter);
    public abstract void Execute(object parameter);
    public event EventHandler CanExecuteChanged;

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

… the reason for this will become clear shortly.

Then the actual command implementation …

public class perRelayCommand : perCommandBase
{
    private readonly Action _execute;
    private readonly Func<bool> _canExecute;

    public perRelayCommand(Action execute) : this(execute, () => true)
    {
    }

    public perRelayCommand(Action execute, Func<bool> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public override bool CanExecute(object parameter) => _canExecute.Invoke();

    public override void Execute(object parameter) => _execute.Invoke();
}

… and a parallel version that takes a typed parameter.

public class perRelayCommand<T> : perCommandBase
{
    private readonly Action<T> _execute;
    private readonly Func<T, bool> _canExecute;

    public perRelayCommand(Action<T> execute) : this(execute, _ => true)
    {
    }

    public perRelayCommand(Action<T> execute, Func<T, bool> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public override bool CanExecute(object parameter) => _canExecute.Invoke((T)parameter);

    public override void Execute(object parameter) => _execute.Invoke((T)
}

There are some implementations of ICommand that use the old style CommandManager object, but I prefer this explicit manual mode of firing CanExecute. Using the CommandManager can degrade application performance – CanExecute is triggered for each defined command after every key stroke or mouse click.

A common way of handing CanExecute is …

public MyViewModel()
{
    DoSomethingCommand = new RelayCommand(() => OnDoSomething(), () => IntProperty > 10); 
}

public RelayCommand DoSomethingCommand { get; }

private void OnDoSomething()
{
    ...
}

private int _intProperty

public int IntProperty
{
    get { return _intProperty; }
    set { 
            if (Set(nameof(IntProperty), ref _intProperty, value))
            {
                DoSomethingCommand.RaiseCanExecuteChanged();
            }
        }
}  

… where DoSomethingCommand can only be executed when IntProperty is greater than 10.

However, I believe that this construct is flawed. The IntProperty setter should have no knowledge of how that property is being used. Also, what happens when your command’s CanExecute function depends on the state of another object, even one that you don’t have the source code for.

To that end, I’ve created perRelayCommandExtender – a helper class for RelayCommands, that allows a command to observe both properties and observable collections (triggering whenever the collection is updated), in any class.

public static class perRelayCommandHelper
{
    // use this when the command is owned by the source object - i.e. we aren't concerned about their relative lifespans
    public static perCommandBase ObservesInternalProperty(this perCommandBase command, INotifyPropertyChanged source, string propertyName)
    {
        source.PropertyChanged += (s, e) =>
        {
            if (string.Equals(e.PropertyName, propertyName, StringComparison.InvariantCultureIgnoreCase))
                command.RaiseCanExecuteChanged();
        };

        return command;
    }

    // use this when the command is owned by an object (ViewModel) other than the source object
    // i.e. we need to take account of their relative lifespans - in particular if the source object is live for the whole application runtime
    public static perCommandBase ObservesExternalProperty(this perCommandBase command, INotifyPropertyChanged source, string propertyName)
    {
        perWeakPropertyChangedEventHandler.Register(source, propertyName, command, (listener, sender, args) => listener.RaiseCanExecuteChanged());

        return command;
    }

    public static perCommandBase ObservesCollection(this perCommandBase command, INotifyCollectionChanged collection)
    {
        collection.CollectionChanged += (s, e) => command.RaiseCanExecuteChanged();

        return command;
    }
}

The methods each return the command instance, allowing calls to be chained if the command depends on multiple conditions. Note that if you are observing an object outside of the current ViewModel then you have to be careful to prevent potential memory leaks. ObservesExternalProperty() uses perWeakPropertyChangedEventHandler as discussed in my previous post. Otherwise, if the observed property is in the same ViewModel as owns the command, then we don’t have to be quite so careful regarding memory leaks. ObservesInternalProperty() just uses a standard PropertyChanged event handler.

It is now possible to write a complex CanExecute function containing multiple clauses, with all the logic in one place. e.g.

DoSomethingCommand = new RelayCommand(()=> OnDoSomething,
                                      ()=> IntProperty > 10
                                           && SomeItem.BoolProperty 
                                           && ItemList.Any())
                            .ObservesInternalProperty(this, nameof(IntProperty))
                            .ObservesExternalProperty(SomeItem, nameof(SomeItem.BoolProperty))
                            .ObservesCollection(ItemList);

Async Commands

A standard RelayCommand is fine when the command calls a quick piece of functionality, but what about when the command triggers a long running operation, such as a call to the data access layer, or any other functionality using the async / await model. In the previous post, the loaded command was declared as …

LoadDataCommand = new RelayCommand(async () => await OnLoadData().ConfigureAwait(false));

… but that looks like a lot of potentially duplicated code. It would also be nice to have the command make CanExecute return false, thus automatically disabling any bound controls, while the long running task is executing. To provide this functionality, I’ve created perRelayCommandAsync in the ViewModel library.

public class perRelayCommandAsync : perCommandBase, INotifyPropertyChanged
{
    private readonly Func<Task> _execute;
    private readonly Func<bool> _canExecute;

    public perRelayCommandAsync(Func<Task> execute) : this(execute, () => true)
    {
    }

    public perRelayCommandAsync(Func<Task> execute, Func<bool> canExecute)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    private bool _isExecuting;

    /// <summary>
    /// Is the command currently executing
    /// </summary>
    public bool IsExecuting
    {
        get => _isExecuting;
        set
        {
            _isExecuting = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsExecuting)));
            RaiseCanExecuteChanged();
        }
    }

    public override bool CanExecute(object parameter) => !IsExecuting && _canExecute();

    public override async void Execute(object parameter)
    {
        if (!CanExecute(parameter))
        {
            return;
        }

        IsExecuting = true;
        try
        {
            var response = await _execute()
                .ExecuteActionWithTimeoutAsync(ExecuteTimeOut)
                .ConfigureAwait(true);

            if (response.IsTimedOut)
            {
                OnTimeOutAction?.Invoke(response);
            }
            else if (response.IsError)
            {
                OnErrorAction?.Invoke(response);
            }
        }
        finally
        {
            IsExecuting = false;
        }
    }

    /// <summary>
    /// Timeout value for Execute invocation
    /// </summary>
    public TimeSpan ExecuteTimeOut { get; set; } = perTimeSpanHelper.Forever;

    /// <summary>
    /// Optional action to perform if Execute generates an error.
    /// </summary>
    public Action<perAsyncActionResponse> OnErrorAction { get; set; }

    /// <summary>
    /// Optional action to perform if Execute times out.
    /// </summary>
    public Action<perAsyncActionResponse> OnTimeOutAction { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
}

So now the commands within the StaffManager application can be declared as …

LoadDataCommand = new perRelayCommandAsync(OnLoadData);

AddPersonCommand = new perRelayCommand(OnAddPerson);

DeletePersonCommand = new perRelayCommand(OnDeletePerson, () => SelectedPersonVm != null)
    .ObservesInternalProperty(this, nameof(SelectedPersonVm));

ListSelectedPeopleCommand = new perRelayCommand(OnListSelectedPeople, ()=>_personVmList.Any())
    .ObservesCollection(_personVmList);

Note that the command properties are still defined as just ICommand, as none of the perRelayCommand specific functionality is exposed outside of the ViewModel.

Automating Commands

Using buttons to fire off processing is fine for the AddPerson, DeletePerson and ListSelectedPeople commands, but it’s not ideal for loading the Person items from the data store. It would be nice to have that functionality called automatically as soon as the View is fully created and active. That state is indicated by the Loaded event of the View. You could write something like this in MainView.Xaml.cs …

public MainView()
{
    DataContext = SimpleIoc.Default.GetInstance<MainViewModel>();
    Loaded += (s, e) => (DataContext as MainViewModel).LoadDataCommand.Execute(null);
    InitializeComponent();
}

… but that promotes very tight coupling between the View and ViewModel classes – not the MVVM way.

These is a better construct though. A combination of the Expression Blend libraries and MVVMLight provides a MVVM pure method to call a bound command, when a specified event is fired in the View.

<Window x:Class="StaffManager.MainView" 
    ... 
    xmlns:cmd="http://www.galasoft.ch/mvvmlight" 
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
    ... >

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <cmd:EventToCommand Command="{Binding LoadDataCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

In order for this to work properly, the View’s constructor must be changed slightly – the DataContext must be set before calling InitializeComponent().

public MainView()
{
    DataContext = SimpleIoc.Default.GetInstance<MainViewModel>();
    InitializeComponent();
}

If you need the event’s argument inside the ViewModel, EventToCommand has a PassEventArgsToCommand property.

This technique can also be used for any event on any child control of the Window.

In my next post, I’ll take a short break from MVVM to look at WPF Behaviors.

Don’t forget that all of the code samples for this blog are available on Github, along with my own personal C# / WPF library.

If you find this article useful, or you have any other feedback, please leave a comment below.

Leave a Reply

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