Функциональность с Range в ObservableCollection

Класс ObservableCollection не позволяет добавлять, удалять и т.д. коллекции элементов.Чтобы добавить такую функциональность можно создать потомок этого класса, в котором реализовать необходимый функционал.В ObservableCollection есть унаследованное от Collection свойство:

protected IList Items { get; } с которым и необходимо работать.Шаблон доработки такой:1) Проверить на возможность изменения:

protected void CheckReentrancy (); 2) Обработать элементы согласно вашей логике: protected IList Items { get; } 3) Вызвать событие PropertyChanged для свойств «Count» и «Item[]»: OnPropertyChanged (new PropertyChangedEventArgs («Count»)); OnPropertyChanged (new PropertyChangedEventArgs («Item[]»)); 4) Вызвать событие CollectionChanged с параметрами события: тип изменения Reset, параметры OldItems и NewItems не передавать: OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset)); Недостатки: Из-за п.4 в обработчике события CollectionChanged невозможно будет работать с OldItems и NewItems так как они пустые. Это необходимо из-за того, что некоторые контролы WPF не работают с изменениями коллекции не по одному элементу, а по несколько. При этом, если тип изменения Reset, то это означает что произошло существенно изменение коллекции, и для контролов WPF это нормально. Если же вы используете новый класс не в качестве источника данных для контрола WPF, то можно в п.4 передавать и другие типы изменений, а также заполненные значения OldItems и NewItems и затем спокойно их обрабатывать.

Пример:

using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Linq;

namespace Common.Utils { public class ObservableRangeCollection: ObservableCollection { private const string CountString = «Count»; private const string IndexerName = «Item[]»;

protected enum ProcessRangeAction { Add, Replace, Remove };

public ObservableRangeCollection () : base () { }

public ObservableRangeCollection (IEnumerable collection) : base (collection) { }

public ObservableRangeCollection (List list) : base (list) { }

protected virtual void ProcessRange (IEnumerable collection, ProcessRangeAction action) { if (collection == null) throw new ArgumentNullException («collection»);

var items = collection as IList? collection.ToList (); if (! items.Any ()) return;

this.CheckReentrancy ();

if (action == ProcessRangeAction.Replace) this.Items.Clear (); foreach (var item in items) { if (action == ProcessRangeAction.Remove) this.Items.Remove (item); else this.Items.Add (item); }

this.OnPropertyChanged (new PropertyChangedEventArgs (CountString)); this.OnPropertyChanged (new PropertyChangedEventArgs (IndexerName)); this.OnCollectionChanged (new NotifyCollectionChangedEventArgs (NotifyCollectionChangedAction.Reset)); }

public void AddRange (IEnumerable collection) { this.ProcessRange (collection, ProcessRangeAction.Add); }

public void ReplaceRange (IEnumerable collection) { this.ProcessRange (collection, ProcessRangeAction.Replace); }

public void RemoveRange (IEnumerable collection) { this.ProcessRange (collection, ProcessRangeAction.Remove); } } } Тесты:

using System; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using Common.Utils; using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Tests.Common { [TestClass] public class ObservableRangeCollectionTests { [TestMethod] public void AddRangeTest () { var eventCollectionChangedCount = 0; var eventPropertyChangedCount = 0;

var orc = new ObservableRangeCollection(new List {0, 1, 2, 3}); orc.CollectionChanged += (sender, e) => { Assert.AreEqual (NotifyCollectionChangedAction.Reset, e.Action); eventCollectionChangedCount++; }; ((INotifyPropertyChanged) orc).PropertyChanged += (sender, e) => { CollectionAssert.Contains (new[] { «Count», «Item[]» }, e.PropertyName); eventPropertyChangedCount++; }; orc.AddRange (new List { 4, 5, 6, 7 });

Assert.AreEqual (8, orc.Count); CollectionAssert.AreEqual (new List { 0, 1, 2, 3, 4, 5, 6, 7 }, orc); Assert.AreEqual (1, eventCollectionChangedCount); Assert.AreEqual (2, eventPropertyChangedCount); }

[TestMethod] public void ReplaceRangeTest () { var eventCollectionChangedCount = 0; var eventPropertyChangedCount = 0;

var orc = new ObservableRangeCollection(new List { 0, 1, 2, 3 }); orc.CollectionChanged += (sender, e) => { Assert.AreEqual (NotifyCollectionChangedAction.Reset, e.Action); eventCollectionChangedCount++; }; ((INotifyPropertyChanged)orc).PropertyChanged += (sender, e) => { CollectionAssert.Contains (new[] { «Count», «Item[]» }, e.PropertyName); eventPropertyChangedCount++; };

orc.ReplaceRange (new List { 4, 5, 6 });

Assert.AreEqual (3, orc.Count); CollectionAssert.AreEqual (new List { 4, 5, 6 }, orc); Assert.AreEqual (1, eventCollectionChangedCount); Assert.AreEqual (2, eventPropertyChangedCount); }

[TestMethod] public void RemoveRangeTest () { var eventCollectionChangedCount = 0; var eventPropertyChangedCount = 0;

var orc = new ObservableRangeCollection(new List { 0, 1, 2, 3 }); orc.CollectionChanged += (sender, e) => { Assert.AreEqual (NotifyCollectionChangedAction.Reset, e.Action); eventCollectionChangedCount++; }; ((INotifyPropertyChanged)orc).PropertyChanged += (sender, e) => { CollectionAssert.Contains (new[] { «Count», «Item[]» }, e.PropertyName); eventPropertyChangedCount++; };

orc.RemoveRange (new List { 1, 3, 6 });

Assert.AreEqual (2, orc.Count); CollectionAssert.AreEqual (new List { 0, 2 }, orc); Assert.AreEqual (1, eventCollectionChangedCount); Assert.AreEqual (2, eventPropertyChangedCount); }

private enum RangeAction { Add, Replace, Remove }

private void EmptyRangeTest (RangeAction action) { var eventCollectionChangedCount = 0; var eventPropertyChangedCount = 0;

var orc = new ObservableRangeCollection(new List { 0, 1, 2, 3 }); orc.CollectionChanged += (sender, e) => { eventCollectionChangedCount++; }; ((INotifyPropertyChanged)orc).PropertyChanged += (sender, e) => { eventPropertyChangedCount++; };

switch (action) { case RangeAction.Replace: orc.ReplaceRange (new List()); break; case RangeAction.Remove: orc.RemoveRange (new List()); break; default: orc.AddRange (new List()); break; }

Assert.AreEqual (4, orc.Count); CollectionAssert.AreEqual (new List { 0, 1, 2, 3 }, orc); Assert.AreEqual (0, eventCollectionChangedCount); Assert.AreEqual (0, eventPropertyChangedCount); }

[TestMethod] public void AddEmptyRangeTest () { this.EmptyRangeTest (RangeAction.Add); }

[TestMethod] public void ReplaceEmptyRangeTest () { this.EmptyRangeTest (RangeAction.Replace); }

[TestMethod] public void RemoveEmptyRangeTest () { this.EmptyRangeTest (RangeAction.Remove); }

[TestMethod] [ExpectedException (typeof (ArgumentNullException))] public void AddNullRangeTest () { new ObservableRangeCollection().AddRange (null); }

[TestMethod] [ExpectedException (typeof (ArgumentNullException))] public void ReplaceNullRangeTest () { new ObservableRangeCollection().ReplaceRange (null); }

[TestMethod] [ExpectedException (typeof (ArgumentNullException))] public void RemoveNullRangeTest () { new ObservableRangeCollection().RemoveRange (null); } } }

© Habrahabr.ru