Use commands in a viewmodel

Completed

Note

.NET MAUI is the next evolution of Xamarin and what we recommend you develop mobile and desktop apps with, and you can learn more about .NET MAUI in several training modules. This Xamarin training module will not be maintained going forward.

You've seen how to get data from your viewmodels to your UI. And you can use two-way binding to get data back from the UI to your viewmodels. You've already seen an example that saves the current ListView selection.

Using two-way bindings like that is the preferred way to react to changes from the UI whenever data changes. Many things that we would handle as events can be handled by using two-way bindings and MVVM. Other examples are things like Switch.IsToggled and Slider.Value, which can be reflected in our viewmodel without having to use events.

But there are some things, like a Button or MenuItem activation, that aren't directly tied to changing data. These interactions still require event-like handling. But we do not want to handle them as Clicked and Selected events in the code-behind, if possible. We want to handle them in our viewmodel and in a way that's testable.

Use the command pattern

Many of the Xamarin.Forms controls that have this kind of interactions support binding to an ICommand property. The button is one example:

<Button Text="Give Bonus" Command="{Binding GiveBonus}">

The Command has a binding to GiveBonus, which implies that this property exists on our viewmodel. The type of that property has to be ICommand or at least implement it. The code would look something like this:

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonus {get; private set;}
    ...
}

The ICommand interface has an Execute method that's called when the button is clicked. In this way, the ICommand.Execute directly replaces Button.Click event-handling code.

The full ICommand interface has two more methods: CanExecute and CanExecuteChanged can be used to determine whether a control should appear enabled or disabled.

A button, for example, might appear dimmed if CanExecute returns false.

Here's what the ICommand interface looks like in C# code:

public interface ICommand
{
    bool CanExecute(object parameter);
    void Execute(object parameter);
    event EventHandler CanExecuteChanged;
}

Use Command<T>

This command pattern lets you maintain clean separation of UI behavior from UI implementation. But it can complicate your code if you need to create a separate class to implement each event handler.

Instead of creating several custom classes that implement the interface, it's common to use Command<T>. This class implements ICommand but exposes its behavior as properties that you can set. This allows you to implement the GiveBonus property that we described earlier entirely within our viewmodel class:

public class EmployeeViewModel : INotifyPropertyChanged
{
    public ICommand GiveBonus {get; private set;}
    public EmployeeViewModel(Employee model)
    {
        GiveBonus = new Command(GiveBonusExecute, GiveBonusCanExecute)
    }

    void GiveBonusExecute()
    {
        //logic for giving bonus
    }

    bool GiveBonusCanExecute()
    {
        //logic for deciding if "give bonus" button should be enabled.
    }
}

In this code, the Execute behavior is provided by the method GiveBonusExecute. And CanExecute is provided by GiveBonusCanExecute. Delegates to those methods are passed to the Command constructor. In this example, there's no implementation for CanExecuteChanged.

Check your knowledge

1.

In Xamarin.Forms, which of the following techniques to handle a button click is best for the testability of your code?

2.

Say you're using Xamarin.Forms to write an app that calculates a 15 percent tip on a restaurant bill. You want to use the MVVM design pattern. Your UI contains an Entry for the user to type in the bill amount and a Button to click to do the calculation. You want to put the logic into your viewmodel, so you decide to use a Command to implement the behavior of the app. How should your viewmodel get the bill amount into the calculation when the command is executed?