2

I went through SO questions on this topic and thought I followed the examples carefully but I still don't see a UI update when a property of an item in a CollectionView gets updated.

First, I made sure the property in my model is decorated with ObservableProperty:


public partial class Student : ObservableObject
{
   public int Id { get; set; }

   public string Name { get; set; }

   [ObservableProperty]
   bool isRegistered; 
}

I then use an ObservableCollection in my view model which is also decorated with ObservableProperty:


public partial class MyViewModel : ObservableObject
{
   public MyViewModel()
   {
      var student = new Student
      {
          Id = 123,
          Name = "John Doe",
          IsRegistered = false
      };
      Students.Add(student);
      student = new Student
      {
          Id = 234,
          Name = "Jane Doe",
          IsRegistered = false
      };
      Students.Add(student);
   }

   [ObservableProperty]
   ObservableCollection<Student> students = new();

   [RelayCommand]
   void UpdateRegistrations()
   {
      foreach(var item in Students)
         item.IsRegistered = true;
   }
}

Here's the XAML:


<ContentPage...>
   <Grid
      RowDefinitions="*,50">

      <CollectionView
         Grid.Row="0"
         ItemsSource="{Binding Students}">
         <CollectionView.ItemTemplate>
            <DataTemplate
               x:DataType="model:Student">
               <Grid
                  ColumnDefinitions="*,30">
                  <Label
                     Grid.Column="0"
                     Text="{Binding Name}" />
                  <Image
                     Grid.Column="1"
                     Source="checkmark.png"
                     IsVisible="{Binding IsRegistered}" />
               </Grid>
            </DataTemplate>
         </CollectionView.ItemTemplate>
      </CollectionView>

      <Button
         Grid.Row="1"
         Text="Update Data"
         Command="{Binding UpdateRegistrationsCommand}" />

   </Grid>
</ContentPage>

I don't see the change in IsRegistered reflected in the UI after executing UpdateRegistrations(). What am I missing here? I'm under the impression that CommunityToolkit.Mvvm handles INotifyPropertyChanged logic. Do I need to handle the change manually?

Sam
  • 26,817
  • 58
  • 206
  • 383
  • Where is the relevant XAML? – Jason Jul 19 '23 at 18:40
  • Nothing fancy going on in the XAML but updated the original post with the XAML code. – Sam Jul 19 '23 at 19:06
  • If you default the value to true does it show? – Jason Jul 19 '23 at 19:26
  • If I set the default value to `true`, the `Image` shows. I also reversed the logic so that when the `Button` is tapped, I set `IsRegistered` to `false`. Again, the UI doesn't reflect the change. – Sam Jul 19 '23 at 20:26
  • What you've done should work; you are correct that ObservableObject/ObservableProperty should propagate that update to UI. Seems suspiciously like a bug with IsVisible. As a test, add another element that is affected by IsRegistered, but not involving IsVisible: ``. SIDE NOTE: Does `IsVisible={Binding IsRegistered}` even compile? I'm used to seeing double-quotes. – ToolmakerSteve Jul 19 '23 at 21:30
  • Sam, please read the comment I’ve added to the accepted answer. That answer has a partially incorrect description. Don’t want you misled. – ToolmakerSteve Jul 20 '23 at 05:56

1 Answers1

1
  1. ObservableCollection doesn't need the [ObservableProperty] attribute; it automatically invokes NotifyPropertyChanged and NotifyCollectionChanged when you call its methods like .Add(). That's kinda the whole reason we use ObservableCollection<T> instead of List<T> for MVVM.

  2. In .NET MAUI (and Xamarin), View Model properties used for bindings must be a public non-static property, so we'll promote Students from a field to a read-only property (aka add { get; } ).

  3. In the XAML, the DataTemplate bindings are using incorrect syntax: the Name binding is missing the Binding keyword, and both should be wrapped in double-quotes ("). These bugs should be invoking a compiler (syntax) error when XAMLC is enabled, so make sure you haven't accidentally disabled XAMLC.

public partial class MyViewModel : ObservableObject
{
   public MyViewModel()
   {
      Students.Add(new Student
      {
          Id = 123,
          Name = "John Doe",
          IsRegistered = false
      });

      Students.Add(new Student
      {
          Id = 234,
          Name = "Jane Doe",
          IsRegistered = false
      });
   }

   public ObservableCollection<Student> Students { get; } = new();

   [RelayCommand]
   void UpdateRegistrations()
   {
      foreach(var item in Students)
         item.IsRegistered = true;
   }
}
<ContentPage...>
   <Grid
      RowDefinitions="*,50">

      <CollectionView
         Grid.Row="0"
         ItemsSource="{Binding Students}">
         <CollectionView.ItemTemplate>
            <DataTemplate
               x:DataType="model:Student">
               <Grid
                  ColumnDefinitions="*,30">
                  <Label
                     Grid.Column="0"
                     Text="{Binding Name}" />
                  <Image
                     Grid.Column="1"
                     Source="checkmark.png"
                     IsVisible="{Binding IsRegistered}" />
               </Grid>
            </DataTemplate>
         </CollectionView.ItemTemplate>
      </CollectionView>

      <Button
         Grid.Row="1"
         Text="Update Data"
         Command="{Binding UpdateRegistrationsCommand}" />

   </Grid>
</ContentPage>

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
  • 2
    "3" is the actual bug fix. 1 and 2 are moot. [ObservableProperty] does the equivalent. Also, 1’s explanation is partially wrong. ObservableCollection DOES NOT call NotifyPropertyChanged when `.Add`. That add does not change the value of the property; the property still points to the same collection. The only special feature of ObservableCollection is its NotifyCollectionChanged. [In fact, if code never does Add/Remove/etc, instead REPLACING the entire collection whenever there is a change, then List works just as well. There are some use cases where replacement is better performance.] – ToolmakerSteve Jul 20 '23 at 06:03