-->

Implement AddRange on ObservableCollection with pr

2020-07-22 19:38发布

问题:

I would like my own descendant of ObservableCollection to support AddRange method. Here is what I currently have:

public class ObservableCollectionPlus<T> : ObservableCollection<T>
{
    public void InsertRange(IEnumerable<T> items)
    {
        this.CheckReentrancy();
        foreach (var item in items) Items.Add(item);

        var type = NotifyCollectionChangedAction.Reset;
        var colChanged = new NotifyCollectionChangedEventArgs(type);
        var countChanged = new PropertyChangedEventArgs("Count");

        OnPropertyChanged(countChanged);
        OnCollectionChanged(colChanged);
    }
}

I don't have much knowledge of what's exactly going on here and why are these events get raised. This is a solutiom that I've assembled after doing some research on google and stackoverflow.

Now, if I bind an instance of my class to say LongListSelector then, after dynamically adding items via InsertRange to ObservableCollectionPlus, a binded LongListSelector's scroll position is sent to it's top.

If I add items in this standard way: foreach (var item in items) collection.Add(item); then LongListSelector's position does not get shifted. But of course this way I get DataBinding notifications overhead in which is undesired.

Apparently, something is done wrong in my current solution. How can I implement InsertRange that will behave exactly like foreach (var item in items) collection.Add(item); but will only fire DataBinding notification once and will not do strange things to binded object's scroll position?

回答1:

It could be because your sending the NotifyCollectionChangedAction.Reset notification, maybe just the NotifyCollectionChangedAction.Add will work, Maybe :)

public class ObservableRangeCollection<T> : ObservableCollection<T>
{
    public void AddRange(IEnumerable<T> collection)
    {
        foreach (var i in collection)
        {
            Items.Add(i);
        }
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, collection.ToList()));
    }
}


回答2:

I've used this in a project recently...

public class RangeObservableCollection<T> : ObservableCollection<T>
{
    private bool _suppressNotification = false;

    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (!_suppressNotification)
            base.OnCollectionChanged(e);
    }

    public void AddRange(IEnumerable<T> list)
    {
        if (list == null)
            throw new ArgumentNullException("list");

        _suppressNotification = true;

        foreach (T item in list)
        {
            Add(item);
        }
        _suppressNotification = false;
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
    }
}


回答3:

The problem you are experiencing with DataBinding might be connected to the fact that you don't raise PropertyChanged for indexer (property name "Item[]") as it happens in ObservableCollection according to the source code.

You can also have a look at a nice implementation of ObservableRangeCollection by James Montemagno here on GitHub, it inherits from ObservableColection and includes AddRange and ReplaceRange methods with all PropertyChaged and CollectionChanged notifications needed for DataBinding.



回答4:

It took me ages, the trouble was always with the arguments to pass to the NotifyCollectionChangedEventArgs ctor. There are many different ctors that take different arguments, depending on the action. The following seems to finally work for me: https://github.com/lolluslollus/Utilz/blob/master/Utilz/SwitchableObservableCollection.cs