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

от автора

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

В ObservableCollection есть унаследованное от Collection свойство:

protected IList<T> Items { get; }

с которым и необходимо работать.

Шаблон доработки такой:
1) Проверить на возможность изменения:

protected void CheckReentrancy();

2) Обработать элементы согласно вашей логике:

protected IList<T> 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<T> : ObservableCollection<T>     {         private const string CountString = "Count";         private const string IndexerName = "Item[]";          protected enum ProcessRangeAction         {             Add,             Replace,             Remove         };          public ObservableRangeCollection() : base()         {         }          public ObservableRangeCollection(IEnumerable<T> collection) : base(collection)         {         }          public ObservableRangeCollection(List<T> list) : base(list)         {         }          protected virtual void ProcessRange(IEnumerable<T> collection, ProcessRangeAction action)         {             if (collection == null) throw new ArgumentNullException("collection");              var items = collection as IList<T> ?? 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<T> collection)         {             this.ProcessRange(collection, ProcessRangeAction.Add);         }          public void ReplaceRange(IEnumerable<T> collection)         {             this.ProcessRange(collection, ProcessRangeAction.Replace);         }          public void RemoveRange(IEnumerable<T> 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<int>(new List<int> {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<int> { 4, 5, 6, 7 });              Assert.AreEqual(8, orc.Count);             CollectionAssert.AreEqual(new List<int> { 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<int>(new List<int> { 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<int> { 4, 5, 6 });              Assert.AreEqual(3, orc.Count);             CollectionAssert.AreEqual(new List<int> { 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<int>(new List<int> { 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<int> { 1, 3, 6 });              Assert.AreEqual(2, orc.Count);             CollectionAssert.AreEqual(new List<int> { 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<int>(new List<int> { 0, 1, 2, 3 });             orc.CollectionChanged += (sender, e) =>             {                 eventCollectionChangedCount++;             };             ((INotifyPropertyChanged)orc).PropertyChanged += (sender, e) =>             {                 eventPropertyChangedCount++;             };              switch (action)             {                 case RangeAction.Replace: orc.ReplaceRange(new List<int>());                     break;                 case RangeAction.Remove: orc.RemoveRange(new List<int>());                     break;                 default: orc.AddRange(new List<int>());                     break;             }              Assert.AreEqual(4, orc.Count);             CollectionAssert.AreEqual(new List<int> { 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<int>().AddRange(null);         }          [TestMethod]         [ExpectedException(typeof(ArgumentNullException))]         public void ReplaceNullRangeTest()         {             new ObservableRangeCollection<int>().ReplaceRange(null);         }          [TestMethod]         [ExpectedException(typeof(ArgumentNullException))]         public void RemoveNullRangeTest()         {             new ObservableRangeCollection<int>().RemoveRange(null);         }     } } 

ссылка на оригинал статьи http://habrahabr.ru/post/253857/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *