MVVM with the MVVM Community Toolkit

When you first create a MAUI application, you might be surprised to find there’s no view models setup by default. I guess this just allows us to see a bare bones application without any preference for MVVM libraries etc.

Let’s try out the MVVM community toolkit with the template generated MAUI app. This toolkit which uses code generators to allow us to create really simple look view model code without all the ceremony…

  • Add the nuget package CommunityToolkit.Mvvm to your references
  • I’m going to store my view models within the ViewModels folder, so add a new folder to your project named ViewModels
  • We’re going to replace the code within MainPage.xaml.cs with our view model, so add a file to ViewModels named MainPageViewModel.cs and it should look like this to start us off
    using CommunityToolkit.Mvvm.ComponentModel;
    
    namespace MauiAppTest.ViewModels;
    
    public partial class MainPageViewModel : ObservableObject
    {
    }
    

    Note: It’s important to make this a partial class, we’ll see why later. The ObservableObject simply gives us the INotifyPropertyChanged and INotifyPropertyChanging implementations.

  • Now within MainPage.xaml.cs, delete the OnCounterClicked method and also delete the line int count = 0;
  • To MainPage.xaml.cs, within the constructor add
    BindingContext = new MainPageViewModel();
    
  • Finally, for now, go to MainPage.xaml and delete the Button’s XAML
    Clicked="OnCounterClicked"
    

At this point we’ve got a bare bones view model which does nothing, so first off we’ll need a property for the count changes and a command which is called to change the count.

  • Within the MainPageViewModel.cs add the following which will create an observable property
    [ObservableProperty] 
    int count;
    
  • Within the MainPage.xaml, change the button’s text to bind to our Count property
    Text="{Binding Count, StringFormat='Click me {0}'}"
    

You might be wondering where the Count property came from, because in our MainPageViewModel.cs with simply declare a field named count (all lowercase and not a property). Well this is the magic of the MVVM toolkit. If you expand the MainPageViewModel.cs in solution explorer you’ll see the class and expanding that you’ll see a Count property, OnCountChanged and OnCountChanging methods where did these come from?

Originally, when we created our view model class, the first important thing is that the class must be a partial class. This allows the next bit of magic, the source generator, to add code to our class without (obviously) altering our class.

When we declare a field using the ObservablePropertyAttribute the MVVM source generator will create the property (with the expected case) along with methods that we could implement for OnXXXChanging and OnXXXChanged. For now let’s look to complete the functionality required to bind the Button to a command and change the Count.

  • Like the ObservableProperty we have another MVVM magical attribute, so add the following code to the MainPageViewModel class
    // you'll need using CommunityToolkit.Mvvm.Input;
    
    [RelayCommand]
    void IncrementCount()
    {
      Count++;
    }
    
  • Now within MainPage.xaml, add the following to the Button XAML
    Command="{Binding IncrementCountCommand}"
    

    Note how the method name is used suffixed with Command when the generator creates our code.

If you try this out, you should see that clicking the button in the UI increments the counter and displays this all via MVVM binding.

We can add code to tell the UI via binding whether the command can be executed by using one of the RelayCommandAttribute’s parameters, CanExecute. Using this we can tell RelayCommand the name of the method to execute, which should return a bool to tell the binding whether we CanExecute the command or now, for example lets add the following to the MainPageViewModel

[ObservableProperty]
bool canIncrement;

bool CanExecuteIncrement() => canIncrement;

and change the existing RelayCommand to

[RelayCommand(CanExecute = nameof(CanExecuteIncrement))]

If you run this now you’ll find that the initial binding of IncrementCountCommand will the CanExecuteIncrement method call returning false and the button will ofcourse be disabled, and we can not enable it at the moment – so let’s just add a simple Checkbox to our MAUI app that binds to CanIncrement, so add before the Button

<CheckBox IsChecked="{Binding CanIncrement}" />

Whilst this will change/toggle the CanIncrement property it will not (as is) tell the binding to essentially re-run the CanExecuteIncrement method. For this to work we just add another attribute to the canIncrement field, making it look like this

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(IncrementCountCommand))]
bool canIncrement;

The addition of NotifyCanExecuteChangedFor will then call IncrementCountCommand.NotifyCanExecuteChanged() and update the UI accordingly.

Earlier we mentioned that code for the property generates extra methods such as OnXXXChanged. Let’s implement our own OnCanIncrementChanged method. These methods are partial so if we add the following to the MainPageViewModel

partial void OnCanIncrementChanged(bool value)
{
  Debug.WriteLine("OnCanIncrementChanged called");
}

Now, when the property CanIncrement changes, this method is called with the current property value passed as a parameter.

References

MVVM source generators
ObservableProperty attribute
RelayCommand attribute