.NET MAUI Documentation

Commands in MVVM

Commands are a fundamental concept in the Model-View-ViewModel (MVVM) architectural pattern, particularly in frameworks like .NET MAUI. They provide a way to decouple user interface actions from the logic that executes those actions. This separation of concerns is a core benefit of MVVM, leading to more maintainable, testable, and flexible applications.

What are Commands?

In MVVM, a Command is an object that encapsulates an action. Instead of directly handling UI events (like button clicks) in the code-behind of your View, you bind these events to a Command exposed by your ViewModel. When the UI event occurs, the framework automatically invokes the associated Command.

This approach offers several advantages:

  • Decoupling: The View is no longer directly aware of the specific action logic.
  • Testability: ViewModels can be tested independently of the UI, as Commands can be executed programmatically.
  • Reusability: Commands can be reused across different Views or even within the same View for multiple UI elements.
  • Enable/Disable Logic: Commands can expose a property (CanExecute) that determines whether they are currently enabled, allowing the UI to automatically update its state (e.g., disable a button).

Implementing Commands with ICommand

In .NET MAUI (and WPF/Xamarin.Forms), the ICommand interface is the standard for implementing commands. It defines two primary methods:

  • Execute(object parameter): This method contains the logic to be performed when the command is invoked.
  • CanExecute(object parameter): This method determines if the command can be executed at the current time. The UI element bound to the command will typically be enabled only if CanExecute returns true.

The ICommand interface also has a CanExecuteChanged event, which is crucial for notifying the UI when the state of CanExecute might have changed.

Using CommandBase or RelayCommand

While you can implement ICommand directly, it's often more convenient to use helper classes. .NET MAUI (and its predecessors) provide base classes or community-driven implementations like RelayCommand for simplifying command creation.

A common pattern involves creating a property in your ViewModel that returns an instance of a command implementation. Here's a simplified example using a conceptual RelayCommand:

ViewModel Example


using CommunityToolkit.Mvvm.Input;
using System.Windows.Input;

public class MyViewModel : ObservableObject
{
    private string _message = "Hello, MVVM!";

    public string Message
    {
        get => _message;
        set => SetProperty(ref _message, value);
    }

    public ICommand ChangeMessageCommand { get; }

    public MyViewModel()
    {
        // Command that always executes
        ChangeMessageCommand = new RelayCommand(ChangeMessage);

        // Command with CanExecute logic
        // IncrementCommand = new RelayCommand(Increment, CanIncrement);
    }

    private void ChangeMessage()
    {
        Message = $"Message changed at {DateTime.Now}";
    }

    // Example for a command that can be conditionally executed
    // private bool CanIncrement()
    // {
    //     // Logic to determine if incrementing is possible
    //     return true; // or some condition
    // }
    //
    // private void Increment()
    // {
    //     // Increment logic
    // }
}
                    

Binding Commands in the View

In your XAML View, you bind UI elements to these commands using the Command property.

XAML View Example


<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewmodels="clr-namespace:MyApp.ViewModels"
             x:Class="MyApp.MyPage"
             x:DataType="viewmodels:MyViewModel"
             Title="MVVM Commands">

    <StackLayout Padding="20" Spacing="10" VerticalOptions="Center">
        <Label Text="{Binding Message}" HorizontalOptions="Center" FontSize="Large"/>

        <Button Text="Change Message"
                Command="{Binding ChangeMessageCommand}"
                HorizontalOptions="Center" />

        <!-- Example of a button that might be enabled/disabled -->
        <!--
        <Button Text="Increment"
                Command="{Binding IncrementCommand}"
                HorizontalOptions="Center" />
        -->
    </StackLayout>

</ContentPage>
                    

When the "Change Message" button is tapped, the ChangeMessageCommand in the ViewModel is executed, updating the Message property. Because the Label is data-bound to Message, its content is automatically updated.

Command Parameters

Commands can also accept parameters. This is useful when the action needs to be aware of the data associated with the UI element triggering it (e.g., passing the text from a TextBox). You can specify a command parameter in XAML using the CommandParameter property or bind it to a property in your ViewModel.

Example with Command Parameter


<!-- Assuming your ViewModel has a command like: -->
<!-- public ICommand SaveItemCommand { get; } -->
<!-- in its constructor: SaveItemCommand = new RelayCommand<string>(SaveItem); -->
<!-- private void SaveItem(string itemContent) { ... } -->

<Entry x:Name="itemEntry" />
<Button Text="Save" Command="{Binding SaveItemCommand}" CommandParameter="{Binding Text, Source={x:Reference itemEntry}}"/>
                    

CommunityToolkit.Mvvm

The Microsoft.Toolkit.Mvvm (now part of the .NET Community Toolkit) provides excellent, performance-optimized MVVM primitives, including ObservableObject for implementing the INotifyPropertyChanged interface and RelayCommand for easily creating ICommand implementations. It's highly recommended for .NET MAUI MVVM development.

By embracing commands, you build more robust, testable, and maintainable .NET MAUI applications following the MVVM pattern.