Writing an asynchronous RelayCommand implementing ICommand


Read the full article here

If you are familiar with MVVM, then you are already familiar with relay commands. We use relay commands to map a method with the ICommand raised from a control. Below is the typical code snippet you may have seen a lot of time.

The class below is pretty simple; it accepts a method and allows you to associate it with a command. Whenever the command is invoked, it is relayed to the method we have passed on as “execute” param. It acts like a disconnected event, if I may say.

    public class RelayCommand : ICommand
    {
        private readonly Action executedMethod;
        private readonly Func<T, bool> canExecuteMethod;
 
        public event EventHandler CanExecuteChanged;
        public RelayCommand(Action execute) : this(execute, null) { }
 
        public RelayCommand(Action execute, Func<T, bool> canExecute)
        {
            this.executedMethod = execute ?? throw new ArgumentNullException("execute");
            this.canExecuteMethod = canExecute;
        }
 
        public bool CanExecute(object parameter) => this.canExecuteMethod == null || this.canExecuteMethod((T)parameter);
        public void Execute(object parameter) => this.executedMethod((T)parameter);
        public void OnCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }

Since Action deals with void functions, we cannot get the async methods working. So I resorted for Func instead of Action and rewrote my RelayCommand to below.

 public class RelayCommandAsync : ICommand
    {
        private readonly Func<T, Task> executedMethod;
        private readonly Func<T, bool> canExecuteMethod;
 
        public event EventHandler CanExecuteChanged;
        public RelayCommandAsync(Func<T, Task> execute) : this(execute, null) { }
 
        public RelayCommandAsync(Func<T, Task> execute, Func<T, bool> canExecute)
        {
            this.executedMethod = execute ?? throw new ArgumentNullException("execute");
            this.canExecuteMethod = canExecute;
        }
 
        public bool CanExecute(object parameter) => this.canExecuteMethod == null || this.canExecuteMethod((T)parameter);
        public async void Execute(object parameter) => await this.executedMethod((T)parameter);
        public void OnCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }

Comments (1)

  1. In our product, we had to have a less naive implementation :
    – as it is asynchronous, if the user spams the button, you don’t want to trigger multiple request… So you need to monitor when the async function is started and when it is done.
    – as now the async function can do something long in the background, the user need to be notified about this.
    We’ve upgrade the ICommand to IAsyncCommand :
    public interface IAsyncCommand : ICommand, INotifyPropertyChanged {
    bool BackgroundOperationRunning { get; set; }
    }

    in that case, we can use the BackgroundOperationRunning to theme the button to have some animation while the async callback is running. The RelayCommandAsync is more complicated but it allows us a nice user experience

Skip to main content