.NET MAUI:派生ObservableCollection不触发UI渲染及实现方式咨询
.NET MAUI派生集合不触发UI更新的问题解决
问题原因
你当前的实现有两个核心问题导致UI不更新:
- 仅监听集合本身的变化,未监听元素属性变化:
[NotifyPropertyChangedFor(nameof(CompletedTodos))]只会在Todos集合被重新赋值、添加/删除元素时触发通知,但当集合内单个TODO的IsCompleted属性变化时,Todos集合本身不会引发PropertyChanged事件,因此CompletedTodos的更新通知不会被发送,UI也就不会重新获取派生集合。 - 每次访问都新建集合实例:
CompletedTodos每次调用都会创建新的ObservableCollection,UI绑定的是第一次获取的集合实例,即便后续新集合有数据,UI也不会自动切换到新实例。
正确实现方式
推荐使用ICollectionView(.NET内置的集合视图),它基于原集合做过滤,无需新建集合,还能自动监听元素属性变化:
ViewModel代码
using System.ComponentModel; using System.Windows.Data; using CommunityToolkit.Mvvm.ComponentModel; public partial class YourViewModel : ObservableObject { [ObservableProperty] ObservableCollection<TODO> todos = new(); public ICollectionView CompletedTodosView { get; } public YourViewModel() { // 创建基于Todos的集合视图 CompletedTodosView = CollectionViewSource.GetDefaultView(Todos); // 设置过滤规则 CompletedTodosView.Filter = item => ((TODO)item).IsCompleted; } } // 确保TODO类实现INotifyPropertyChanged public class TODO : INotifyPropertyChanged { private bool _isCompleted; public bool IsCompleted { get => _isCompleted; set { _isCompleted = value; OnPropertyChanged(nameof(IsCompleted)); } } public event PropertyChangedEventHandler? PropertyChanged; private void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } // 其他属性... }
XAML代码
<CollectionView ItemsSource="{Binding CompletedTodosView}"/>
替代方案(维护独立ObservableCollection)
如果必须使用独立的ObservableCollection,需要手动监听集合和元素的变化:
public partial class YourViewModel : ObservableObject { [ObservableProperty] ObservableCollection<TODO> todos = new(); private readonly ObservableCollection<TODO> _completedTodos = new(); public ObservableCollection<TODO> CompletedTodos => _completedTodos; public YourViewModel() { Todos.CollectionChanged += Todos_CollectionChanged; UpdateCompletedTodos(); } private void Todos_CollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) { // 订阅/取消订阅元素的PropertyChanged事件 if (e.NewItems != null) { foreach (TODO item in e.NewItems) item.PropertyChanged += Todo_PropertyChanged; } if (e.OldItems != null) { foreach (TODO item in e.OldItems) item.PropertyChanged -= Todo_PropertyChanged; } UpdateCompletedTodos(); } private void Todo_PropertyChanged(object? sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(TODO.IsCompleted)) UpdateCompletedTodos(); } private void UpdateCompletedTodos() { _completedTodos.Clear(); foreach (var todo in Todos.Where(t => t.IsCompleted)) _completedTodos.Add(todo); } }
关于实现方式的选择
当前的实现方式不正确,不需要使用Behavior/Converter,ICollectionView是最简洁高效的方案,它原生支持集合过滤、排序和分组,且能自动同步UI。
内容的提问来源于stack exchange,提问作者rohanharikr
相关产品推荐
相关产品推荐

