WinUI 3 + CommunityToolkit.Mvvm: Desktop MVVM Without the Boilerplate
Building a WinUI 3 desktop app with proper MVVM used to mean writing mountains of INotifyPropertyChanged plumbing and ICommand implementations. The CommunityToolkit.Mvvm source generators change everything: you write a few attributes, and the compiler generates all the notification and command wiring for you.
Let’s build a simple counter app. First, the ViewModel (notice how little code there is):
using CommunityToolkit.Mvvm.ComponentModel;using CommunityToolkit.Mvvm.Input;
namespace CounterApp.ViewModels;
public partial class MainViewModel : ObservableObject{ [ObservableProperty] private int _count;
[ObservableProperty] private string _message = "Click the button!";
[RelayCommand] private void Increment() { Count++; Message = Count switch { < 10 => $"Count is {Count}. Keep going!", < 50 => $"Count is {Count}. You're on a roll! 🔥", _ => $"Count is {Count}. Okay, you can stop now. 😅" }; }
[RelayCommand] private void Reset() { Count = 0; Message = "Reset! Ready for another round?"; }}[ObservableProperty] on a field generates a public property with OnPropertyChanged calls baked in. [RelayCommand] on a method generates an IRelayCommand property (e.g., IncrementCommand) that you can bind directly in XAML. Zero boilerplate.
Here’s the XAML that binds to it using x:Bind:
<Window x:Class="CounterApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:vm="using:CounterApp.ViewModels">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" RowSpacing="16"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{x:Bind ViewModel.Message, Mode=OneWay}" FontSize="24" TextAlignment="Center"/>
<Button Grid.Row="1" Content="Increment" Command="{x:Bind ViewModel.IncrementCommand}" HorizontalAlignment="Center"/>
<Button Grid.Row="2" Content="Reset" Command="{x:Bind ViewModel.ResetCommand}" HorizontalAlignment="Center"/> </Grid></Window>And the code-behind is just wiring up the ViewModel:
using Microsoft.UI.Xaml;using CounterApp.ViewModels;
namespace CounterApp;
public sealed partial class MainWindow : Window{ public MainViewModel ViewModel { get; } = new();
public MainWindow() { InitializeComponent(); }}That’s a fully functional, properly architected MVVM app in about 50 lines total. The source generators handle all the PropertyChanged plumbing, command creation, and even CanExecute logic if you need it. Your ViewModels stay focused on behavior, not infrastructure. Welcome to the future of Windows desktop development.