如何避免在替换所有元素或添加元素集合时多次触发ObservableCollection.CollectionChanged

Pet*_*Lee 42 c# silverlight observablecollection

我有ObservableCollection<T>集合,我想用新的元素集合替换所有元素,我可以这样做:

collection.Clear(); 
Run Code Online (Sandbox Code Playgroud)

要么:

collection.ClearItems();
Run Code Online (Sandbox Code Playgroud)

(顺便说一句,这两种方法有什么区别?)

我也foreach可以collection.Add一个接一个地使用,但这会多次发射

添加元素集合时也一样.

编辑:

我在这里找到了一个很好的库:增强的ObservableCollection能够延迟或禁用通知,但它似乎不支持silverlight.

Jeh*_*hof 58

ColinE的所有信息都是正确的.我只想添加我ObservableCollection用于此特定情况的子类.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • +1为记住为Count和Items []提出PropertyChanged我将不得不更新我在我自己的项目中使用的代码;-) (16认同)
  • @Aaron你需要在`Count`和`Item []`上引发属性更改.一些观察者可能会检查这些属性.看一下ObservableCollection的实现,您将看到操作集合的所有方法都会引发这些属性的事件. (2认同)

Col*_*inE 10

您可以通过继承ObservableCollection和实现自己的ReplaceAll方法来实现此目的.此方法的实现将替换内部Items属性中的所有项,然后触发CollectionChanged事件.同样,您可以添加AddRange方法.有关此实现,请参阅此问题的答案:

ObservableCollection不支持AddRange方法,因此除了INotifyCollectionChanging外,我还会收到添加的每个项目的通知?

Collection.Clear和之间的区别Collection.ClearItems是,它Clear是一个公共API方法,虽然ClearItems受到保护,但它是一个扩展点,允许您扩展/修改它的行为Clear.


Pet*_*Lee 5

以下是我为其他人的参考实现的内容:

// 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));
    }
}
Run Code Online (Sandbox Code Playgroud)