Jebarson's dev blog

Coffee, Code

Writing an asynchronous RelayCommand implementing ICommand

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 canExecuteMethod;

        public event EventHandler CanExecuteChanged;
        public RelayCommand(Action execute) : this(execute, null) { }

        public RelayCommand(Action execute, Func 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 executedMethod;
        private readonly Func canExecuteMethod;

        public event EventHandler CanExecuteChanged;
        public RelayCommandAsync(Func execute) : this(execute, null) { }

        public RelayCommandAsync(Func execute, Func 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);
    }

As you can see above I have mentioned that my “executemethod” will accept a type T. You can remove T if you are pointing to a method without parameter. It will return “Task” which is what an async methods will return.

Now lets put the code into to action and see how it can be consumed.

        public ICommand PreviousCommand { get; set; }

        public ICommand NextCommand { get; set; }

        public MyViewModel()
        {
            this.PreviousCommand = new RelayCommand(this.NavigatePrevious);
            this.NextCommand = new RelayCommandAsync(this.NavigateNext);
        }

        private void NavigatePrevious(RoutedEventArgs obj)
        {
            this.IsBusy = true;
            // Cannot call this method asynchronously.
            Task.Delay(new TimeSpan(0,0,5));
            this.IsBusy = false;
        }

        private async Task NavigateNext(RoutedEventArgs obj)
        {
            this.IsBusy = true;
            // Can call this method asynchronously.
            await Task.Delay(new TimeSpan(0, 0, 5));
            this.IsBusy = false;
        }

We have got two commands “PreviousCommand” and “NextCommand” registering a synchronous “NavigatePrevious” and an asynchronous “NavigateNext” method respectively.

Writing an asynchronous RelayCommand implementing ICommand

Leave a Reply

Scroll to top