暂停对C#和WPF中ObservableCollection的更新
暂停对C#和WPF中ObservableCollection的更新
我有一个使用MVVM数据绑定的WPF应用程序。我正在向ObservableCollection<...>
添加项目,而且确实有很多项目。
现在我想知道,每次我向集合中添加一个项目时,它是否立即触发事件并导致不必要的开销?如果是这样的话,我是否可以暂时禁用事件通知,并在我的代码结束时手动触发它,这样如果我添加了10,000个项目,则只会触发一次,而不是10,000次?
更新:我尝试使用这个类:
using System; using System.Linq; using System.Collections.Specialized; using System.Collections.Generic; namespace MyProject { ////// 表示在项目添加、删除或整个列表刷新时提供通知的动态数据集合。 /// ///public class ObservableCollection : System.Collections.ObjectModel.ObservableCollection { /// /// 将指定集合的元素添加到ObservableCollection(Of T)的末尾。 /// public void AddRange(IEnumerablecollection) { foreach (var i in collection) Items.Add(i); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection.ToList())); } /// /// 从ObservableCollection(Of T)中删除指定集合中的每个项目的第一个匹配项。 /// public void RemoveRange(IEnumerablecollection) { foreach (var i in collection) Items.Remove(i); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, collection.ToList())); } /// /// 清除当前集合并用指定项替换它。 /// public void Replace(T item) { ReplaceRange(new T[] { item }); } ////// 清除当前集合并用指定集合替换它。 /// public void ReplaceRange(IEnumerablecollection) { List old = new List (Items); Items.Clear(); foreach (var i in collection) Items.Add(i); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, collection.ToList())); } /// /// 初始化 System.Collections.ObjectModel.ObservableCollection(Of T) 类的新实例。 /// public ObservableCollection() : base() { } ////// 初始化 System.Collections.ObjectModel.ObservableCollection(Of T) 类的新实例,并从指定的集合中复制元素。 /// /// collection: 要复制元素的集合。 ///collection 参数不能为 null。 public ObservableCollection(IEnumerablecollection) : base(collection) { } } }
现在我得到了这个错误:
附加信息:不支持范围操作。
错误发生在这里:
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection.ToList()));
使用以下方式可以非常快速和简单地对ObservableCollection进行子类化,并在调用AddRange时暂停通知。请参考以下博客文章以了解更多详情。
public class SuspendableObservableCollection: ObservableCollection { private bool _suspendCollectionChangeNotification; protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (!_suspendCollectionChangeNotification) { base.OnCollectionChanged(e); } } public void AddRange(IEnumerable items) { if (items == null) { throw new ArgumentNullException(nameof(items)); } _suspendCollectionChangeNotification = true; try { foreach (var item in items) { Add(item); } } finally { _suspendCollectionChangeNotification = false; OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } } }
原因:ObservableCollection在调用AddRange时会触发多次CollectionChanged事件,而有时候我们希望暂停这些通知,以提高性能或避免重复操作。
解决方法:创建一个SuspendableObservableCollection类,继承自ObservableCollection,并添加一个AddRange方法。在AddRange方法中,我们通过设置_suspendCollectionChangeNotification标志来暂停通知,然后遍历items并逐个调用Add方法将其添加到集合中。最后,我们将_suspendCollectionChangeNotification标志重置为false,并手动触发CollectionChanged事件以通知UI进行更新。
这样,我们就可以使用SuspendableObservableCollection类来暂停通知,并在需要的时候一次性添加多个项到集合中,而不会触发多次CollectionChanged事件。
在C#和WPF中暂停更新ObservableCollection
这个实现将所有被抑制的通知替换为重置通知。这在逻辑上是合理的。当用户抑制通知,进行批量更改,然后重新启用时,发送一个重置通知是恰当的。
下面是实现的代码:
public class ObservableCollectionEx: ObservableCollection { private bool _notificationSupressed = false; private bool _supressNotification = false; public bool SupressNotification { get { return _supressNotification; } set { _supressNotification = value; if (_supressNotification == false && _notificationSupressed) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); _notificationSupressed = false; } } } protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if (SupressNotification) { _notificationSupressed = true; return; } base.OnCollectionChanged(e); } }
这个实现非常干净、简洁!在Visual Studio 2019中进行的样本测试中,将WPF控件绑定到一个包含几十行的ObservableCollection