Search code examples
c#xamlmvvmmauicommunity-toolkit-mvvm

Maui MVVM: most simple pattern not working


I am working on a tiny addition of a project using the MVVM pattern.

The project is a little bit bigger, but I stripped it down to a tiny sample project to make stuff easier here.

Code

BusyViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;

namespace MauiApp6.ViewModels
{
    public partial class BusyViewModel : ObservableObject
    {
        [ObservableProperty]
        private bool isBusy = false;
    }
}

CommandViewModel.cs

using CommunityToolkit.Mvvm.Input;

namespace MauiApp6.ViewModels
{
    public partial class CommandViewModel
    {
        private BusyViewModel busyViewModel;

        public CommandViewModel()
        {
            // Initialize BusyViewModel
            busyViewModel = new BusyViewModel();
        }

        [RelayCommand()]
        internal void Click()
        {
            // Set IsBusy to true
            busyViewModel.IsBusy = true;
        }
    }
}

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"
             xmlns:vm="clr-namespace:MauiApp6.ViewModels"
             x:Class="MauiApp6.MainPage" >

    <StackLayout>
        <Button Text="Click Me" 
                BindingContext="{Binding Source={vm:CommandViewModel}}"
                Command="{Binding ClickCommand}"/>

        <Label BindingContext="{Binding Source={vm:BusyViewModel}}"
               Text="{Binding IsBusy}"/>

        <ActivityIndicator BindingContext="{Binding Source={vm:BusyViewModel}}"
                           IsRunning="{Binding IsBusy}"/>
    </StackLayout>
</ContentPage>

Expected Result

After clicking on the button
  • The ClickCommand fires
  • The text of the label changes to 'true'
  • The ActivityIndicator starts spinning

Actual Result

After clicking the button
  • The ClickCommand fires
  • No visible change happens

Question

  • What is the part I am missing? I can't find the - most likely very tiny - difference to the uncountable examples out in the web.

Solution

  • The problem is you have two BusyViewModel instances, the view model you created for the Label and the view model embedded in the CommandViewModel. the ClickCommand changes in the one embedded in the CommandViewModel, and it does not change in the BusyViewModel of the label so no visible change will happen.

    the solution is as @Liyun Zang - MSFT suggested, you should make one instance of the CommandViewModel and set the command of the button to this instance's ClickCommand property. and set the 'BindingContext' label of the Label and the ActivityIndicator to this instances BusyViewModel` property

    public MainPage()
    {
      InitializeComponent();
      // instance will be set to all the elements inside MainPage
      BindingContext= new CommandViewModel();
    }
    
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:vm="clr-namespace:MauiApp6.ViewModels"
                 x:Class="MauiApp6.MainPage" >
    
        <StackLayout>
           <!--because BindingContext is set for the MainPage no need set it here again and setting it again here will lead to unexpected behaviour-->
            <Button Text="Click Me"
                    Command="{Binding ClickCommand}"/>
            <!--here BindingContext will be set to CommandViewModel.BusyViewModel property-->
            <Label BindingContext="{Binding BusyViewModel}"
                   Text="{Binding IsBusy}"/>
            <!--same thing-->
            <ActivityIndicator BindingContext="{Binding Source={vm:BusyViewModel}}"
                               IsRunning="{Binding IsBusy}"/>
        </StackLayout>
    </ContentPage>