Maui MVVM - NotifyPropertyChangedFor not working with complex object and its child property

55 views Asked by At

I'm trying to use CommunityToolkit.MVVM in a MAUI 8 project. I'm facing difficulty with notifying using NotifyPropertyChangedFor.

The ViewModel has a boolean variable CanSubmitForm, which only gets scoped at the beginning when the VM is appearing (before my API call completes) and not after it. I've decorated a complex object with NotifyPropertyChangedFor, and the object is getting populated after the API returned values. I'm trying to enable the button after StudentInfo and its child objects are populated and Gradconditions (child object) are met:

The complex object looks like:

Model:

public class StudentInfo
{
    public string Id {get; set;}
    public string? StudentName {get; set;}
    public string StudentAddress {get; set;}
    public List<Checks>? Gradconditions {get; set;}
}

In my ViewModel I've following properties:

ViewModel:

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanSubmitForm))]
private StudentInfo? student;

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanSubmitForm))]
private int conditionsMet;

I also have a boolean variable to enable/disable the form submission button:

public bool CanSubmitForm
{
    get => ConditionsMet == Student.Gradconditions.Count;
}

And then I have a method to submit the form:

[RelayCommand(CanExecute = nameof(CanSubmitForm))]
private void SubmitForm()
{
    /*Do housekeeping and submit form*/
}

On page load method, I'm populating all fields on the complex StudentInfo object from an API call, and then if conditions in my CanSubmitForm are met, I'm enabling the button for form submission.

public async Task OnAppearingAsync(string studentId)
{
    /* populate StudentInfo object and ConditionsMet from API */

}

On the page, I've the button like so:

View:

<Button 
    Text = "Submit"
    Command="{Binding SubmitFormCommand}"
/>

What am I doing wrong? My suspicion is that I can't perform NotifyPropertyChangedFor on child objects and propagate them to the parent object that has the decoration. How can I achieve the desired result of NotifyPropertyChangedFor on the count of the child object, which is a list?

1

There are 1 answers

0
Jessie Zhang -MSFT On

There are several problems with the code you shared.

First, since you want to enable the Button(Submit) based on the value of property CanSubmitForm, you need to add ObservableProperty and NotifyCanExecuteChangedFor for property CanSubmitForm

    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SubmitFormCommand))]
    public bool canSubmitForm = false;

Second, if we add some items to property public List<Checks>? Gradconditions {get; set;}, we cannot notify property Student in the view model simply with the following code,even if you define it as public ObservableCollection<string>? radconditions = new ObservableCollection<string>();:

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(CanSubmitForm))]
private StudentInfo? student;

Based on your code ,I created a demo and tried to implement basic functions.

I added another Button to add some fake data to the Radconditions to simulate this scenario.

You can refer to the following code:

public partial class MyViewModel : ObservableObject
{
    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(CanSubmitForm))]
    private StudentInfo? student = new StudentInfo();

    [ObservableProperty]
    [NotifyPropertyChangedFor(nameof(CanSubmitForm))]
    private int conditionsMet = 2;


    [ObservableProperty]
    [NotifyCanExecuteChangedFor(nameof(SubmitFormCommand))]
    public bool canSubmitForm = false;

    //public bool CanSubmitForm
    //{
    //    get => ConditionsMet == Student.Gradconditions.Count;
    //}


    [RelayCommand(CanExecute = nameof(CanSubmitForm))]
    private void SubmitForm()
    {
        /*Do housekeeping and submit form*/

    }
    [RelayCommand]
    private void AddItems() {

        Student.Gradconditions.Add("1");
        Student.Gradconditions.Add("2");

        if (Student.Gradconditions.Count == ConditionsMet) {

            CanSubmitForm = true;

        }else
        {
            CanSubmitForm = false;
        }

    }
}

StudentInfo.cs

public partial class StudentInfo : ObservableObject
{
    public string Id { get; set; }
    public string? StudentName { get; set; }
    public string StudentAddress { get; set; }

    [ObservableProperty]       
    public ObservableCollection<string>? gradconditions = new ObservableCollection<string>();

    //public List<string>? Gradconditions { get; set; } =new List<string>();
}

Usage example:

<?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:MauiApp323.ViewModels"
             x:Class="MauiApp323.MainPage">
    <ContentPage.BindingContext>
        <vm:MyViewModel></vm:MyViewModel>
    </ContentPage.BindingContext>

    <VerticalStackLayout
            Padding="30,0"
            Spacing="25">

            <Button  Text = "Submit" Command="{Binding SubmitFormCommand}"/>

            <Button Text="add data to list" Command="{Binding AddItemsCommand}"/>

    </VerticalStackLayout>
</ContentPage>