MVVM – Commands

Make it So, Number One

I already included a very basic ICommand implementation in the initial version of StaffManager. In this post, I’m going to look at a couple of ways to use the full power of commands to add functionality to the application.

The ICommand interface contains three elements

  • Execute – an action to invoke when the command is triggered
  • CanExecute – an optional rule 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

MVVMLight has its own implementation of ICommand – RelayCommand, along with a second version that takes a typed parameter. One important note is to avoid using the WPF specific version of these commands, as they reference the old style CommandManager object – CanExecute checks are handled in the background, and can be triggered for all defined commands at any time, perhaps even after each key press or mouse click. Add the namespace GalaSoft.MvvmLight.Command, rather then GalaSoft.MvvmLight.CommandWPF to your ViewModel code file. Using this version of the RelayCommand class does requires you to call RaiseCanExecuteChanged() manually as required though, but I’ll cover a way to minimise this shortly.

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 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. 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 : perViewModelBase, ICommand
    {
        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)
        {
            if (execute == null)
                throw new ArgumentNullException(nameof(execute));

            _execute = execute;
            _canExecute = canExecute;
        }

        private bool _isExecuting;

        public bool IsExecuting
        {
            get { return _isExecuting; }
            set { 
                    if (Set(nameof(IsExecuting), ref _isExecuting, value))
                        RaiseCanExecuteChanged();
                }
        }

        public event EventHandler CanExecuteChanged;

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

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

            IsExecuting = true;
            await _execute().ConfigureAwait(true);
            IsExecuting = false;
        }

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

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

    LoadDataCommand = new perRelayCommandAsync(OnLoadData);

    AddPersonCommand = new RelayCommand(OnAddPerson);

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

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

Note that the command properties are still defined as just ICommand, as none of the RelayCommand 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 really need the event’s argument inside the ViewModel, EventToCommand has a PassEventArgsToCommand property. In that case, the command should be defined as RelayCommand, where T is the type of the event argument.

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 *