C# 如何避免在替换所有元素或添加元素集合时触发ObservableCollection.CollectionChanged多次
我有C# 如何避免在替换所有元素或添加元素集合时触发ObservableCollection.CollectionChanged多次,c#,silverlight,observablecollection,C#,Silverlight,Observablecollection,我有observateCollectioncollection,我想用一个新的元素集合替换所有元素,我可以做: collection.Clear(); 或: (顺便问一下,这两种方法有什么区别?) 我也可以使用foreach来collection.Add 添加元素集合时也是如此 编辑: 我在这里找到了一个很好的库:但它似乎不支持silverlight。您可以通过子类化ObservableCollection并实现自己的ReplaceAll方法来实现这一点。此方法的实现将替换内部items属性
observateCollection
collection,我想用一个新的元素集合替换所有元素,我可以做:
collection.Clear();
或:
(顺便问一下,这两种方法有什么区别?)
我也可以使用foreach
来collection.Add
添加元素集合时也是如此
编辑:
我在这里找到了一个很好的库:但它似乎不支持silverlight。您可以通过子类化
ObservableCollection
并实现自己的ReplaceAll
方法来实现这一点。此方法的实现将替换内部items
属性中的所有项,然后触发CollectionChanged
事件。同样,您可以添加AddRange
方法。有关此问题的实现,请参见此问题的答案:
Collection.Clear
和Collection.ClearItems
之间的区别在于Clear
是一种公共API方法,而ClearItems
是受保护的,它是一个扩展点,允许您扩展/修改Clear
的行为observateCollection
子类
public class SmartCollection<T> : ObservableCollection<T> {
public SmartCollection()
: base() {
}
public SmartCollection(IEnumerable<T> collection)
: base(collection) {
}
public SmartCollection(List<T> list)
: base(list) {
}
public void AddRange(IEnumerable<T> range) {
foreach (var item in range) {
Items.Add(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void Reset(IEnumerable<T> range) {
this.Items.Clear();
AddRange(range);
}
}
公共类SmartCollection:ObservableCollection{
公共SmartCollection()
:base(){
}
公共SmartCollection(IEnumerable集合)
:基本(集合){
}
公共SmartCollection(列表)
:基本(列表){
}
public void AddRange(IEnumerable range){
foreach(范围内的var项目){
项目。添加(项目);
}
此.OnPropertyChanged(新PropertyChangedEventArgs(“计数”);
此.OnPropertyChanged(新的PropertyChangedEventArgs(“项目[]”);
this.OnCollectionChanged(新建NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
公共无效重置(IEnumerable范围){
this.Items.Clear();
AddRange(范围);
}
}
以下是我实施的内容,供其他人参考:
// http://stackoverflow.com/questions/13302933/how-to-avoid-firing-observablecollection-collectionchanged-multiple-times-when-r
// http://stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i-get-notified-for-each
public class ObservableCollectionFast<T> : ObservableCollection<T>
{
public ObservableCollectionFast()
: base()
{
}
public ObservableCollectionFast(IEnumerable<T> collection)
: base(collection)
{
}
public ObservableCollectionFast(List<T> list)
: base(list)
{
}
public virtual void AddRange(IEnumerable<T> collection)
{
if (collection.IsNullOrEmpty())
return;
foreach (T item in collection)
{
this.Items.Add(item);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
// Cannot use NotifyCollectionChangedAction.Add, because Constructor supports only the 'Reset' action.
}
public virtual void RemoveRange(IEnumerable<T> collection)
{
if (collection.IsNullOrEmpty())
return;
bool removed = false;
foreach (T item in collection)
{
if (this.Items.Remove(item))
removed = true;
}
if (removed)
{
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
// Cannot use NotifyCollectionChangedAction.Remove, because Constructor supports only the 'Reset' action.
}
}
public virtual void Reset(T item)
{
this.Reset(new List<T>() { item });
}
public virtual void Reset(IEnumerable<T> collection)
{
if (collection.IsNullOrEmpty() && this.Items.IsNullOrEmpty())
return;
// Step 0: Check if collection is exactly same as this.Items
if (IEnumerableUtils.Equals<T>(collection, this.Items))
return;
int count = this.Count;
// Step 1: Clear the old items
this.Items.Clear();
// Step 2: Add new items
if (!collection.IsNullOrEmpty())
{
foreach (T item in collection)
{
this.Items.Add(item);
}
}
// Step 3: Don't forget the event
if (this.Count != count)
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
//http://stackoverflow.com/questions/13302933/how-to-avoid-firing-observablecollection-collectionchanged-multiple-times-when-r
// http://stackoverflow.com/questions/670577/observablecollection-doesnt-support-addrange-method-so-i-get-notified-for-each
公共类ObservableCollectionFast:ObservableCollection
{
公共可观测集合FAST()
:base()
{
}
公共可观测集合FAST(IEnumerable集合)
:基本(集合)
{
}
公共ObservableCollectionFast(列表)
:基本(列表)
{
}
公共虚拟void AddRange(IEnumerable集合)
{
if(collection.IsNullOrEmpty())
返回;
foreach(集合中的T项)
{
此.Items.Add(item);
}
此.OnPropertyChanged(新PropertyChangedEventArgs(“计数”);
此.OnPropertyChanged(新的PropertyChangedEventArgs(“项目[]”);
this.OnCollectionChanged(新建NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
//无法使用NotifyCollectionChangedAction.Add,因为构造函数仅支持“重置”操作。
}
公共虚拟void RemoveRange(IEnumerable集合)
{
if(collection.IsNullOrEmpty())
返回;
bool-removed=false;
foreach(集合中的T项)
{
如果(本项目移除(项目))
删除=真;
}
如果(已删除)
{
此.OnPropertyChanged(新PropertyChangedEventArgs(“计数”);
此.OnPropertyChanged(新的PropertyChangedEventArgs(“项目[]”);
this.OnCollectionChanged(新建NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
//无法使用NotifyCollectionChangedAction.Remove,因为构造函数仅支持“重置”操作。
}
}
公共虚拟无效重置(T项)
{
重置(新列表(){item});
}
公共虚拟无效重置(IEnumerable集合)
{
if(collection.IsNullOrEmpty()&&this.Items.IsNullOrEmpty())
返回;
//步骤0:检查集合是否与此完全相同。项
if(IEnumerabluetils.Equals(集合,此.Items))
返回;
int count=this.count;
//步骤1:清除旧项目
this.Items.Clear();
//步骤2:添加新项目
如果(!collection.IsNullOrEmpty())
{
foreach(集合中的T项)
{
此.Items.Add(item);
}
}
//第三步:不要忘记这件事
if(this.Count!=Count)
此.OnPropertyChanged(新PropertyChangedEventArgs(“计数”);
此.OnPropertyChanged(新的PropertyChangedEventArgs(“项目[]”);
this.OnCollectionChanged(新建NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
我还不能对之前的答案发表评论,因此我在这里添加了对上述SmartCollection实现的Removatange改编,它不会引发C#InvalidOperationException:Collection已修改。它使用一个谓词来检查是否应该删除该项,在我的例子中,这比创建满足删除条件的项子集更为优化
public void RemoveRange(Predicate<T> remove)
{
// iterates backwards so can remove multiple items without invalidating indexes
for (var i = Items.Count-1; i > -1; i--) {
if (remove(Items[i]))
Items.RemoveAt(i);
}
this.OnPropertyChanged(new PropertyChangedEventArgs("Count"));
this.OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
在过去几年中,我使用了一种更通用的解决方案,通过创建批处理更改操作并使用重置操作通知观察者来消除过多的ObservableCollection通知:
public class ExtendedObservableCollection<T>: ObservableCollection<T>
{
public ExtendedObservableCollection()
{
}
public ExtendedObservableCollection(IEnumerable<T> items)
: base(items)
{
}
public void Execute(Action<IList<T>> itemsAction)
{
itemsAction(Items);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
公共类ExtendedObservableCollection:ObservableCollection
{
公共扩展的ObservableCollection()
{
}
公共扩展可观察集合(IEnumerable items)
:基础(ite)
LogEntries.RemoveRange(i => closeFileIndexes.Contains(i.fileIndex));
public class ExtendedObservableCollection<T>: ObservableCollection<T>
{
public ExtendedObservableCollection()
{
}
public ExtendedObservableCollection(IEnumerable<T> items)
: base(items)
{
}
public void Execute(Action<IList<T>> itemsAction)
{
itemsAction(Items);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
var collection = new ExtendedObservableCollection<string>(new[]
{
"Test",
"Items",
"Here"
});
collection.Execute(items => {
items.RemoveAt(1);
items.Insert(1, "Elements");
items.Add("and there");
});