Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何避免在替换所有元素或添加元素集合时触发ObservableCollection.CollectionChanged多次_C#_Silverlight_Observablecollection - Fatal编程技术网

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");
});