.NET MAUI修改ObservableCollection项属性后CollectionView未刷新
问题现象
- 将
ObservableCollection实例绑定到CollectionView控件后,对集合执行新增、删除项操作时,CollectionView可正常响应刷新 - 通过代码修改集合内单个项的属性值时,界面不会同步展示更新后的内容
- 项目已引入
CommunityToolkit.Mvvm库,预期工具包自动完成属性变更通知逻辑,但实际未生效 - 项目公开仓库地址:TestCollectionBinding
现有实现代码
1. Views/MyItem.cs
namespace TestCollectionBinding.Views; public class MyItem { public string Name { get; set; } public string Device { get; set; } }
2. ViewModels/MainPageViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel; using System; using System.Collections.Generic; using System.Collections.ObservableCollection; 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" } }; public ICommand ChangeCommand => new Command(ChangeControl); public void ChangeControl() { foreach (MyItem q in MyItems) { q.Device = "***********"; } Console.WriteLine($"Change device"); } }
3. 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>
运行表现
页面初始加载时可正常展示2条列表数据,点击「Change device names」按钮触发命令后,代码已将所有MyItem实例的Device属性修改为***********,但CollectionView未同步更新显示内容,预期界面可实时响应属性变更刷新。
问题原因
ObservableCollection仅在集合本身发生变更(新增、删除、替换项、重置集合)时触发CollectionChanged通知,不会监控集合内单个项的属性变化。要让项属性修改触发界面刷新,集合内的元素类必须自身实现INotifyPropertyChanged接口,在属性值变更时发出PropertyChanged通知。
当前代码中MyItem是普通POCO类,未实现属性变更通知逻辑。CommunityToolkit.Mvvm不会自动为普通类注入通知逻辑,只有继承工具包提供的基类(如ObservableObject)、并配合源生成器标注的类,才会自动生成属性变更通知代码。
修复方案
修改MyItem类,继承CommunityToolkit.Mvvm.ComponentModel.ObservableObject,给需要触发变更通知的属性加上[ObservableProperty]标注。注意工具包的源生成器要求类必须声明为partial,被标注的成员为私有字段,采用小驼峰命名,源生成器会自动生成对应大驼峰命名的公开属性,和原有业务代码的属性命名完全兼容,不需要修改其他业务逻辑。
修改后的MyItem.cs
using CommunityToolkit.Mvvm.ComponentModel; namespace TestCollectionBinding.Views; public partial class MyItem : ObservableObject { [ObservableProperty] private string name; [ObservableProperty] private string device; }
注意:现有ViewModel代码中
ObservableCollection的命名空间引用存在笔误,正确命名空间为System.Collections.ObjectModel,需要修正using引用,否则会出现编译错误。
修改完成后重新编译运行,点击按钮修改项属性时界面就会正常同步刷新。
内容的提问来源于stack exchange,提问作者gfmoore

