Search code examples
mauimaui-community-toolkit

Maui App - Page is not updating observable property


I'm experiencing a problem with my Maui App. I'm trying to bind an Application.Score to a label and the value is not refreshing, even though I'm seeing the value refreshing in the viewModel.

Here you can find my xaml page:

<?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:model="clr-namespace:FAZSO.Model"
              xmlns:viewmodel="clr-namespace:FAZSO.ViewModel"
              x:Class="FAZSO.MainPage"
              Title="FAZSO"
              x:DataType="viewmodel:MainPageViewModel">

    <VerticalStackLayout Margin="0,52,0,0" Spacing="20">

        <Grid Padding="5" HorizontalOptions="FillAndExpand">

            <Label Text="FAZ SÓ!"
                    FontSize="26"
                    HorizontalOptions="CenterAndExpand"
                    VerticalOptions="CenterAndExpand" />

                <Label Text="{Binding ApplicationPlayer.Score}"
                        FontSize="26"
                        HorizontalOptions="CenterAndExpand"
                        VerticalOptions="CenterAndExpand" />

        </Grid>

        <CollectionView ItemsSource="{Binding Habits}" SelectionMode="None" ItemsLayout="VerticalGrid, 2">
            <CollectionView.ItemTemplate>

                <DataTemplate x:DataType="model:Habit">

                    <Grid Padding="5" HorizontalOptions="FillAndExpand">

                        <Frame HeightRequest="150" WidthRequest="160" Style="{StaticResource CardView}" Margin="10">
                            <VerticalStackLayout BackgroundColor="{Binding ButtonColor}" Margin="-1">
                                <Image Source="achievement.svg" WidthRequest="100" HeightRequest="100" Margin="-10" />
                                <Label Margin="0" Text="{Binding Name}" FontSize="Medium" TextColor="WhiteSmoke" WidthRequest="100" HeightRequest="50" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" />
                            </VerticalStackLayout>
                            <Frame.GestureRecognizers>
                                <TapGestureRecognizer Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:MainPageViewModel}}, Path=ExecuteButtonCommand}"
                                                    CommandParameter="{Binding .}" />
                            </Frame.GestureRecognizers>
                        </Frame>

                    </Grid>

                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>

</ContentPage>

and here you can find my viewModel:


using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using FAZSO.ViewModel;
using System.ComponentModel;
using System.Diagnostics;

namespace FAZSO.ViewModel;

[QueryProperty(nameof(ApplicationPlayer), "applicationPlayer")]
public partial class MainPageViewModel : BaseViewModel
{
    [ObservableProperty]
    Player applicationPlayer = new();

    public ObservableCollection<Habit> Habits { get; set; } = new();

    public MainPageViewModel()
    {
        Title = "FAZ SO";

        Player player1 = new Player();
        player1.Name = "Player1";

        applicationPlayer = player1;

        Habit habit = new()
        {
            ID = 1,
            Name = "Ler",
            Type = "Mandatory",
            ButtonColor = Color.FromRgb(71, 8, 178), 
        };

        Habit habit1 = new()
        {
            ID = 2,
            Name = "Exercício",
            Type = "Mandatory",
            ButtonColor = Color.FromRgb(71, 8, 178),
        };

        Habit habit2 = new()
        {
            ID = 3,
            Name = "Tocar Guitarra",
            Type = "Mandatory",
            ButtonColor = Color.FromRgb(71, 8, 178),
        };


        Habit habit5 = new()
        {
            ID = 4,
            Name = "Ler Artigo",
            Type = "Mandatory",
            ButtonColor = Color.FromRgb(71, 8, 178),
        };

        Habit habit6 = new()
        {
            ID = 5,
            Name = "Correr",
            Type = "Optional",
            ButtonColor = Color.FromRgb(128, 128, 128),
        };

        Habit habit7 = new()
        {
            ID = 6,
            Name = "Codar",
            Type = "Optional",
            ButtonColor = Color.FromRgb(128, 128, 128),
        };
        Habits.Add(habit);
        Habits.Add(habit1);
        Habits.Add(habit2);
        Habits.Add(habit5);
        Habits.Add(habit6);
        Habits.Add(habit7);

        applicationPlayer.Habits = Habits.ToList();

    }

    [RelayCommand]
    public async Task ExecuteButton(Habit habit)
    {
        try
        {
            habit.Name = "Done";
            habit.ButtonColor = Color.FromRgb(0, 177, 64);
            ApplicationPlayer.Score += 1;

            var index = Habits.IndexOf(Habits.FirstOrDefault(x => x.ID == habit.ID));
            Habits[index] = habit;
        }
        catch (Exception ex)
        {
            await Shell.Current.DisplayAlert("Error!", ex.Message, "OK");
        }
    }
}

Do you have any tip why this is not working?

just for reference, this is my Player class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FAZSO.Model
{
    public class Player
    {
        public List<Habit> Habits { get; set; }

        public string Name { get; set; }

        public int Score { get; set; }

    }
}

Thanks

I've tried to debug this problem using debug mode in viewModel. I can see the value refreshing but not in the xaml page


Solution

  • As Jason said that inherit Player from ObservableObject:

    public class Player : ObservableObject
    {
        public List<Habit> Habits { get; set; }
    
        public string Name { get; set; }
    
        public int Score { get; set; }
    }
    

    In additon, you can also add an If statement to determine whether the habit has been clicked, and if so, return directly:

    public async Task ExecuteButton(Habit habit)
    {
        try 
        {
            if (habit.Name == "Done") 
            {
                return;
            }
            habit.Name = "Done";
            habit.ButtonColor = Color.FromRgb(0, 177, 64);
            ApplicationPlayer.Score += 1;
            ....
    }