Commands

The MVVM (Model View ViewModel) pattern is the de facto standard for developing UI applications using XAML. It is also widely used in Xamarin applications using the native UI approach instead of Xamarin Forms.

Whatever the UI stack we are using amongst those, there is always the concept of a Command. A command, implements the ICommand interface which has been around since .NET 3.0.

This concept and its first implementations predates the creation of async await.

In most applications we would have code like this:

public class MainViewModel : ViewModelBase
{
    private bool _isBusy;
    public bool IsBusy
    {
        get => _isBusy;
        private set => Set(ref _isBusy, value);
    }

    public RelayCommand Submit { get; private set; }    

    public MainViewModel()
    {
        Submit = new RelayCommand(ExecuteSubmit, CanExecuteSubmit);
    }

    private void ExecuteSubmit()
    {
        IsBusy = true;
        // Do something
        IsBusy = false;
    }

    private bool CanExecuteSubmit()
    {
        return true;
    }
}

Here I am using the MvvmLight toolkit so the class implementing ICommand is RelayCommand. Other frameworks could have other names such as DelegateCommand or ActionCommand.

Asynchronism

As we can see in the code above, running asynchronous code inside the ExecuteSubmit method would force us to mark it as async which is a really bad idea.

If you have no idea why async void is a bad idea please refer to the previous post.

Introducing AsyncCommand

We are not forced to use a RelayCommand and we can craft our own ICommand implementation:

public interface IAsyncCommand : ICommand
{
    Task ExecuteAsync();
    bool CanExecute();
}

public class AsyncCommand : IAsyncCommand
{
    public event EventHandler CanExecuteChanged;

    private bool _isExecuting;
    private readonly Func<Task> _execute;
    private readonly Func<bool> _canExecute;
    private readonly IErrorHandler _errorHandler;

    public AsyncCommand(
        Func<Task> execute,
        Func<bool> canExecute = null,
        IErrorHandler errorHandler = null)
    {
        _execute = execute;
        _canExecute = canExecute;
        _errorHandler = errorHandler;
    }

    public bool CanExecute()
    {
        return !_isExecuting && (_canExecute?.Invoke() ?? true);
    }

    public async Task ExecuteAsync()
    {
        if (CanExecute())
        {
            try
            {
                _isExecuting = true;
                await _execute();
            }
            finally
            {
                _isExecuting = false;
            }
        }

        RaiseCanExecuteChanged();
    }

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

#region Explicit implementations
    bool ICommand.CanExecute(object parameter)
    {
        return CanExecute();
    }

    void ICommand.Execute(object parameter)
    {
        ExecuteAsync().FireAndForgetSafeAsync(_errorHandler);
    }
#endregion
}

This implementation has some key features:

  • It does not allow concurrent execution so as to pass monkey testing.
  • It provides explicit implementations in order to be bindable with XAML.
  • It uses the FireAndForgetSafeAsync extension method and the IErrorHandler interface introduced in the previous post to deal with the async void issues when using XAML binding.
  • It only publicly expose the ExecuteAsync providing the Task for cases when the command is not invoked through binding.

This command is not the answer for all async issues but it is a simple one for most of the cases I have encountered.

Using AsyncCommand

With everything in place, we can modify the view model to use the AsyncCommand:

public class MainViewModel : ViewModelBase
{
    private bool _isBusy;
    public bool IsBusy
    {
        get => _isBusy;
        private set => Set(ref _isBusy, value);
    }

    public IAsyncCommand Submit { get; private set; }

    public MainViewModel()
    {
        Submit = new AsyncCommand(ExecuteSubmitAsync, CanExecuteSubmit);
    }

    private async Task ExecuteSubmitAsync()
    {
        try
        {
            IsBusy = true;
            var coffeeService = new CoffeeService();
            await coffeeService.PrepareCoffeeAsync();
        }
        finally
        {
            IsBusy = false;
        }
    }

    private bool CanExecuteSubmit()
    {
        return !IsBusy;
    }
}

As you can see, there is no async void in the view model !

Invoking the command

The command is easily invoked either by:

  • Binding
    <Button
        Text="Give me coffee !" 
        Command="{Binding Submit}"
        VerticalOptions="End" />
    
  • Code
    public void OnPrepareButtonClick(object sender, EventArgs e)
    {
      IErrorHandler errorHandler = null; // Get an instance somewhere
      ViewModel.Submit.ExecuteAsync().FireAndForgetSafeAsync(errorHandler);
    }
    

Generic version

For all commands that need a parameter we can use the following generic version:

public interface IAsyncCommand<T> : ICommand
{
    Task ExecuteAsync(T parameter);
    bool CanExecute(T parameter);
}

public class AsyncCommand<T> : IAsyncCommand<T>
{
    public event EventHandler CanExecuteChanged;

    private bool _isExecuting;
    private readonly Func<T, Task> _execute;
    private readonly Func<T, bool> _canExecute;
    private readonly IErrorHandler _errorHandler;

    public AsyncCommand(Func<T, Task> execute, Func<T, bool> canExecute = null, IErrorHandler errorHandler = null)
    {
        _execute = execute;
        _canExecute = canExecute;
        _errorHandler = errorHandler;
    }

    public bool CanExecute(T parameter)
    {
        return !_isExecuting && (_canExecute?.Invoke(parameter) ?? true);
    }

    public async Task ExecuteAsync(T parameter)
    {
        if (CanExecute(parameter))
        {
            try
            {
                _isExecuting = true;
                await _execute(parameter);
            }
            finally
            {
                _isExecuting = false;
            }
        }

        RaiseCanExecuteChanged();
    }

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

#region Explicit implementations
    bool ICommand.CanExecute(object parameter)
    {
        return CanExecute((T)parameter);
    }

    void ICommand.Execute(object parameter)
    {
        ExecuteAsync((T)parameter).FireAndForgetSafeAsync(_errorHandler);
    }
#endregion
}

The two version are pretty similar and it is tempting to only keep the latter. We could use a AsyncCommand<object> with null parameter for replace the first one. While it technically works, it is better to keep the two of them in the sense that having no parameter is not semantically similar to taking a null parameter.

Conclusion

By writing a simple custom command that natively handles asynchronism, we are able to simplify and improve our code and our application’s stability. Embedding the FireAndForgetSafeAsync method inside the command removes the possibility that we forget to handle exceptions.

Please do not forget that crashes are not acceptable and are entirely our fault.

The source code of this post is available on my GitHub.

As always, please feel free to read my previous posts and to comment below, I will be more than happy to answer.

Comments