Working with Commands in .NET MAUI MVVM

Commands are a fundamental concept in the MVVM pattern, allowing you to decouple user interface actions from the underlying logic. In .NET MAUI, you can leverage the ICommand interface and its implementations to achieve this separation.

What are Commands?

A command is an object that encapsulates an action. It typically has two main components:

By using commands, your UI elements (like Buttons) can be directly bound to these command objects, making your code cleaner and more testable.

Implementing Commands

The most common way to implement commands in .NET MAUI MVVM is by using the ICommand interface. The MVVM pattern often uses an implementation like RelayCommand or DelegateCommand for simplicity.

Using RelayCommand

RelayCommand (or similar implementations found in community toolkits like MVVM Toolkit) is a convenient class that allows you to easily bind a method to an ICommand.

Step 1: Create the ViewModel

Define a ViewModel with a property that exposes an ICommand.


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

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

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

    public ICommand ChangeMessageCommand { get; }

    public MyViewModel()
    {
        ChangeMessageCommand = new RelayCommand(ExecuteChangeMessage, CanExecuteChangeMessage);
    }

    private void ExecuteChangeMessage()
    {
        Message = "Message Changed!";
    }

    private bool CanExecuteChangeMessage()
    {
        // Example: Only allow execution if Message is not already "Message Changed!"
        return _message != "Message Changed!";
    }
}
                

Note: ObservableObject and RelayCommand are typically part of the CommunityToolkit.Mvvm NuGet package. Make sure to add it to your project.

Step 2: Bind the Command in the View

In your XAML view, bind a UI element (like a Button) to the ICommand property of your ViewModel.


<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:viewmodels="clr-namespace:YourApp.ViewModels"
             x:Class="YourApp.Views.MainPage"
             x:DataType="viewmodels:MyViewModel">

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

Step 3: Set the DataContext

Ensure your View's BindingContext is set to an instance of your ViewModel.


// MainPage.xaml.cs
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        BindingContext = new MyViewModel();
    }
}
                

Passing Parameters to Commands

Commands can also accept parameters. This is useful when the action depends on specific data.

ViewModel with Parameterized Command


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

public class ItemViewModel : ObservableObject
{
    public string ItemName { get; set; }
    public ICommand SelectItemCommand { get; }

    public ItemViewModel(string itemName)
    {
        ItemName = itemName;
        SelectItemCommand = new RelayCommand<string>(ExecuteSelectItem);
    }

    private void ExecuteSelectItem(string selectedItemName)
    {
        // Do something with the selected item name
        System.Diagnostics.Debug.WriteLine($"Selected item: {selectedItemName}");
    }
}
            

Binding a Command with a Parameter

You can pass a static parameter or bind a parameter from the UI.


<!-- Assuming your ViewModel has a collection of ItemViewModel objects -->
<ListView ItemsSource="{Binding Items}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="viewmodels:ItemViewModel">
            <ViewCell>
                <StackLayout Padding="10">
                    <Label Text="{Binding ItemName}" />
                    <Button Text="Select"
                            Command="{Binding SelectItemCommand}"
                            CommandParameter="{Binding ItemName}" />
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>
            

The CanExecute Method in Action

The CanExecute method is crucial for providing a responsive user experience. For instance, a "Save" button should be disabled if there are no changes to save.

When the CanExecute condition changes, you typically need to notify the command that its executability might have changed. RelayCommand provides a way to do this:


// In your ViewModel:
public void UpdateCanExecute()
{
    // If you are using RelayCommand, you can invoke this:
    ((RelayCommand)ChangeMessageCommand).NotifyCanExecuteChanged();
}
            

Call UpdateCanExecute() whenever a property changes that might affect the CanExecute logic.

Benefits of Using Commands

Mastering commands is a key step in building robust and maintainable .NET MAUI applications using the MVVM pattern.