What you will learn

  • Explain Model–View–ViewModel
  • Bind Entry and Label controls to ViewModel properties
  • Execute a ViewModel command without a Clicked event handler
How to use this example: Start from a new .NET MAUI App project called MauiMvvm with .NET 10. Use the complete file listings below, and leave other template-generated files unchanged unless the lesson explicitly says otherwise. Run the Android target on Windows.

1. Create a project and install the toolkit

Create MauiMvvm using the .NET MAUI App template. Right-click the project, choose Manage NuGet Packages, search for the CommunityToolkit.Mvvm package, install a version compatible with your project, and restore packages. This package is separate from CommunityToolkit.Maui.

2. Create the ViewModel

Add a folder named ViewModels and a C# class named GreetingViewModel.cs. This example uses ObservableObject and SetProperty directly instead of generated partial properties, making the data flow easy to read.

Name is bound two-way to an Entry. Greeting is bound to a Label. GreetCommand is bound to the Button.

3. Connect the view to the ViewModel

Replace the two MainPage files shown below. The constructor sets BindingContext to a new ViewModel instance. When the button executes the command, SetProperty notifies the UI that the greeting has changed.

4. Verify the binding

  1. Run the project on Android.
  2. Type a name in the Entry.
  3. Tap Update greeting.
  4. Observe that the Label updates even though MainPage has no button-click handler.

Complete sample code

Use the exact file names and folder locations shown below. Copy each listing into your MAUI project. The separate source files can also be downloaded individually.

ViewModels/GreetingViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;

namespace MauiMvvm.ViewModels;

public class GreetingViewModel : ObservableObject
{
    private string name = "";
    private string greeting = "Enter a name to begin.";

    public string Name
    {
        get => name;
        set => SetProperty(ref name, value);
    }

    public string Greeting
    {
        get => greeting;
        set => SetProperty(ref greeting, value);
    }

    public IRelayCommand GreetCommand { get; }

    public GreetingViewModel()
    {
        GreetCommand = new RelayCommand(Greet);
    }

    private void Greet()
    {
        Greeting = string.IsNullOrWhiteSpace(Name)
            ? "Hello from MVVM!"
            : $"Welcome, {Name.Trim()}!";
    }
}
MainPage.xaml
<?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"
             x:Class="MauiMvvm.MainPage"
             Title="MVVM Example">
    <VerticalStackLayout Padding="24" Spacing="16">
        <Label Text="MVVM Greeting" FontSize="26"
               FontAttributes="Bold" />
        <Entry Placeholder="Your name"
               Text="{Binding Name, Mode=TwoWay}" />
        <Button Text="Update greeting"
                Command="{Binding GreetCommand}" />
        <Label Text="{Binding Greeting}" FontSize="20" />
    </VerticalStackLayout>
</ContentPage>
MainPage.xaml.cs
using MauiMvvm.ViewModels;

namespace MauiMvvm;

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        BindingContext = new GreetingViewModel();
    }
}

Common problems and solutions

  • Namespace not found for CommunityToolkit: Install the CommunityToolkit.Mvvm NuGet package and restore packages.
  • Bindings show empty values: Check BindingContext in MainPage.xaml.cs and property names in the XAML.

Practice exercise

Add an observable Counter property and an IncrementCommand to the ViewModel, then bind them to a label and button.

Expected result: Text and commands update correctly through a ViewModel rather than code-behind events.

Official references