I'm trying to update actual fields in an ObservableCollection that is bound to a CollectionView.
The CollectionView updates fine on adding, removing items, but not if I programmatically change an item in the list.
I understand from this post Observable collection not updated that I need to implement INotifyPropertyChanged.
I am using the CommunityToolkit.Mvvm and had hoped that this kind of magic would be automatically done, but it appears not. I don't have the C# knowledge to know how to do what I want. Could someone help me please :)
There is a repo at https://github.com/gfmoore/TestCollectionBinding
and here is my current code:
Views.MyItem.cs
namespace TestCollectionBinding.Views;
public class MyItem
{
public string Name { get; set; }
public string Device { get; set; }
}
ViewModels.MainPageViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using TestCollectionBinding.Views;
namespace TestCollectionBinding.ViewModels;
public partial class MainPageViewModel : ObservableObject
{
[ObservableProperty]
public ObservableCollection<MyItem> myItems = new()
{
new MyItem
{
Name = "Heart Rate Monitor",
Device = "12:12:12:12:AB"
},
new MyItem
{
Name = "Cadence",
Device = "34:34:34:34:CD"
}
};
//show details
public ICommand ChangeCommand => new Command(ChangeControl);
public void ChangeControl()
{
//change device
foreach (MyItem q in MyItems)
{
q.Device = "***********";
}
Console.WriteLine($"Change device");
}
}
and the 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:local="clr-namespace:TestCollectionBinding.ViewModels"
x:Class="TestCollectionBinding.MainPage">
<ContentPage.BindingContext>
<local:MainPageViewModel />
</ContentPage.BindingContext>
<StackLayout>
<Label Text="Items"
FontSize="20"
TextColor="Blue"/>
<CollectionView x:Name="MyItemsList"
ItemsSource="{Binding MyItems}">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid
Margin="10, 0, 10, 10"
ColumnDefinitions="200, 200">
<Label Grid.Column="0"
Text="{Binding Name}"
FontSize="20"/>
<Label Grid.Column="1"
Text="{Binding Device}"
FontSize="20"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Button
Text="Change device names"
FontFamily="20"
WidthRequest="150"
Command="{Binding ChangeCommand}" />
</StackLayout>
</ContentPage>
So the MainPage displays two items in the list and when I hit the button the command cycles through the list and just replaces the Device property with "*********".
I expect to see these changes in the displayed list.
Oh for the days of ... dBase II lol
G
CodePudding user response:
from the docs
public class MyItem : ObservableObject
{
private string name;
public string Name
{
get => name;
set => SetProperty(ref name, value);
}
}
CodePudding user response:
Following the helpful hint from @Jason (ta) I searched a bit harder and found this amazingly helpful explanation at the source: https://docs.microsoft.com/en-us/windows/communitytoolkit/mvvm/observableobject
My MyItem.cs now looks like:
public partial class MyItem : ObservableObject
{
private string name;
private string device;
public string Name {
get => name;
set => SetProperty(ref name, value);
}
public string Device {
get => device;
set => SetProperty(ref device, value);
}
}
It's more complex than I expected it to look... I have to ask though, why is this stuff so impenetrable, I thought coding was supposed to get easier over the years? I would have expected that if a device is connected to a list then it should just (magically) work without all the plumbing that should be hidden from view. If you don't want anything to change make it readonly! :sigh. One day when I'm dead and buried...