Реализуйте AddRange на ObservableCollection с надлежащей поддержкой DataBinding

Я хотел бы, чтобы мой собственный потомок ObservableCollection поддерживать AddRange метод. Вот что у меня сейчас есть:

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

У меня нет особых знаний о том, что именно здесь происходит и почему возникают эти события. Это решение, которое я собрал после некоторых исследований Google и Stackru.

Теперь, если я свяжу экземпляр моего класса, чтобы сказать LongListSelector затем, после динамического добавления элементов через InsertRange в ObservableCollectionPlusв переплете LongListSelectorПозиция прокрутки отправляется наверх.

Если я добавлю элементы стандартным способом: foreach (var item in items) collection.Add(item); затем LongListSelectorпозиция не сдвигается. Но, конечно, таким образом, я получаю уведомления о связывании данных, что нежелательно.

Видимо, что-то не так в моем текущем решении. Как я могу реализовать InsertRange который будет вести себя так же, как foreach (var item in items) collection.Add(item); но будет ли запускать уведомление DataBinding только один раз и не будет делать странные вещи с позицией прокрутки связанного объекта?

4 ответа

Это может быть потому, что вы отправляете NotifyCollectionChangedAction.Reset уведомление, может быть, просто NotifyCollectionChangedAction.Add будет работать, может быть:)

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

Я использовал это в проекте недавно...

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

Проблема, с которой вы сталкиваетесь при использовании DataBinding, может быть связана с тем, что вы не вызываете PropertyChanged для индексатора (имя свойства "Item[]"), как это происходит в ObservableCollection в соответствии с исходным кодом.

Вы также можете взглянуть на хорошую реализацию ObservableRangeCollection Джеймса Монтемагно здесь, на GitHub, она наследуется от ObservableColection и включает методы AddRange и ReplaceRange со всеми уведомлениями PropertyChaged и CollectionChanged, необходимыми для DataBinding.

Это занимало у меня много времени, проблема всегда была в том, что аргументы передавались в ctor NotifyCollectionChangedEventArgs. Есть много разных ctors, которые принимают разные аргументы в зависимости от действия. Следующее, кажется, наконец-то работает для меня: https://github.com/lolluslollus/Utilz/blob/master/Utilz/SwitchableObservableCollection.cs

Другие вопросы по тегам