Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Collections/3.3/ObservableList.cs @ 13597

Last change on this file since 13597 was 13597, checked in by abeham, 9 years ago

#2575: fixed multiple enumeration of collection in ObservableCollection and ObservableList

File size: 17.1 KB
RevLine 
[2572]1#region License Information
2/* HeuristicLab
[12012]3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
[2572]4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
[13597]22using HeuristicLab.Common;
23using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
[2572]24using System;
25using System.Collections;
26using System.Collections.Generic;
[2620]27using System.ComponentModel;
[6342]28using System.Linq;
[2572]29
30namespace HeuristicLab.Collections {
[3560]31  [StorableClass]
[2572]32  [Serializable]
[2574]33  public class ObservableList<T> : IObservableList<T> {
[3560]34    [Storable]
[3286]35    protected List<T> list;
[2572]36
37    #region Properties
38    public int Capacity {
39      get { return list.Capacity; }
[2620]40      set {
41        if (list.Capacity != value) {
42          list.Capacity = value;
43          OnPropertyChanged("Capacity");
44        }
45      }
[2572]46    }
47    public int Count {
48      get { return list.Count; }
49    }
50    bool ICollection<T>.IsReadOnly {
51      get { return ((ICollection<T>)list).IsReadOnly; }
52    }
53
54    public T this[int index] {
55      get {
56        return list[index];
57      }
58      set {
59        T item = list[index];
[2745]60        if (!((item == null) && (value == null)) && ((item == null) || (!item.Equals(value)))) {
[2620]61          list[index] = value;
62          OnItemsReplaced(new IndexedItem<T>[] { new IndexedItem<T>(index, value) }, new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
63          OnPropertyChanged("Item[]");
64        }
[2572]65      }
66    }
67    #endregion
68
69    #region Constructors
70    public ObservableList() {
71      list = new List<T>();
72    }
73    public ObservableList(int capacity) {
74      list = new List<T>(capacity);
75    }
76    public ObservableList(IEnumerable<T> collection) {
77      list = new List<T>(collection);
78    }
[3560]79    [StorableConstructor]
80    protected ObservableList(bool deserializing) { }
[2572]81    #endregion
82
83    #region Access
84    public List<T> GetRange(int index, int count) {
85      return list.GetRange(index, count);
86    }
87
88    public bool Contains(T item) {
89      return list.Contains(item);
90    }
91
92    public int IndexOf(T item) {
93      return list.IndexOf(item);
94    }
95    public int IndexOf(T item, int index) {
96      return list.IndexOf(item, index);
97    }
98    public int IndexOf(T item, int index, int count) {
99      return list.IndexOf(item, index, count);
100    }
101
102    public int LastIndexOf(T item) {
103      return list.LastIndexOf(item);
104    }
105    public int LastIndexOf(T item, int index) {
106      return list.LastIndexOf(item, index);
107    }
108    public int LastIndexOf(T item, int index, int count) {
109      return list.LastIndexOf(item, index, count);
110    }
111
112    public int BinarySearch(T item) {
113      return list.BinarySearch(item);
114    }
115    public int BinarySearch(T item, IComparer<T> comparer) {
116      return list.BinarySearch(item, comparer);
117    }
118    public int BinarySearch(int index, int count, T item, IComparer<T> comparer) {
119      return list.BinarySearch(index, count, item, comparer);
120    }
121
122    public bool Exists(Predicate<T> match) {
123      return list.Exists(match);
124    }
125
126    public T Find(Predicate<T> match) {
127      return list.Find(match);
128    }
129    public List<T> FindAll(Predicate<T> match) {
130      return list.FindAll(match);
131    }
132    public T FindLast(Predicate<T> match) {
133      return list.FindLast(match);
134    }
135
136    public int FindIndex(Predicate<T> match) {
137      return list.FindIndex(match);
138    }
139    public int FindIndex(int startIndex, Predicate<T> match) {
140      return list.FindIndex(startIndex, match);
141    }
142    public int FindIndex(int startIndex, int count, Predicate<T> match) {
143      return list.FindIndex(startIndex, count, match);
144    }
145
146    public int FindLastIndex(Predicate<T> match) {
147      return list.FindLastIndex(match);
148    }
149    public int FindLastIndex(int startIndex, Predicate<T> match) {
150      return list.FindLastIndex(startIndex, match);
151    }
152    public int FindLastIndex(int startIndex, int count, Predicate<T> match) {
153      return list.FindLastIndex(startIndex, count, match);
154    }
155    #endregion
156
157    #region Manipulation
158    public void Add(T item) {
[2620]159      int capacity = list.Capacity;
[2572]160      list.Add(item);
[8610]161      OnItemsAdded(new IndexedItem<T>[] { new IndexedItem<T>(list.Count - 1, item) });
162      OnItemsAdded(new T[] { item });
[2620]163      if (list.Capacity != capacity)
164        OnPropertyChanged("Capacity");
165      OnPropertyChanged("Item[]");
166      OnPropertyChanged("Count");
[2572]167    }
168    public void AddRange(IEnumerable<T> collection) {
[2620]169      int capacity = list.Capacity;
[2572]170      int index = list.Count;
171      List<IndexedItem<T>> items = new List<IndexedItem<T>>();
172      foreach (T item in collection) {
173        items.Add(new IndexedItem<T>(index, item));
174        index++;
175      }
[13597]176      list.AddRange(items.Select(x => x.Value));
[2620]177      if (items.Count > 0) {
[8610]178        OnItemsAdded(items);
[13597]179        OnItemsAdded(items.Select(x => x.Value));
[2620]180        if (list.Capacity != capacity)
181          OnPropertyChanged("Capacity");
182        OnPropertyChanged("Item[]");
183        OnPropertyChanged("Count");
184      }
[2572]185    }
186
187    public void Insert(int index, T item) {
[2620]188      int capacity = list.Capacity;
[2572]189      list.Insert(index, item);
[8610]190      OnItemsAdded(new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
191      OnItemsAdded(new T[] { item });
[2620]192      if (list.Capacity != capacity)
193        OnPropertyChanged("Capacity");
194      OnPropertyChanged("Item[]");
195      OnPropertyChanged("Count");
[2572]196    }
197    public void InsertRange(int index, IEnumerable<T> collection) {
[2620]198      int capacity = list.Capacity;
[2572]199      list.InsertRange(index, collection);
200      List<IndexedItem<T>> items = new List<IndexedItem<T>>();
201      foreach (T item in collection) {
202        items.Add(new IndexedItem<T>(index, item));
203        index++;
204      }
[2620]205      if (items.Count > 0) {
[8610]206        OnItemsAdded(items);
207        OnItemsAdded(collection);
[2620]208        if (list.Capacity != capacity)
209          OnPropertyChanged("Capacity");
210        OnPropertyChanged("Item[]");
211        OnPropertyChanged("Count");
212      }
[2572]213    }
214
[6342]215    /// <summary>
216    /// Performs a Clear and an AddRange, but does not fire separate events for those operations
217    /// </summary>
218    /// <param name="collection"></param>
219    public void Replace(IEnumerable<T> collection) {
220      List<IndexedItem<T>> oldItems = null;
221      if (list.Any()) oldItems = list.Select((x, i) => new IndexedItem<T>(i, x)).ToList();
222      else oldItems = new List<IndexedItem<T>>();
223
224      int oldCapacity = list.Capacity;
225      list.Clear();
226      list.AddRange(collection);
227
228      List<IndexedItem<T>> items = null;
229      if (list.Any()) items = list.Select((x, i) => new IndexedItem<T>(i, x)).ToList();
230      else items = new List<IndexedItem<T>>();
231
[8610]232      OnItemsReplaced(items, oldItems);
[6342]233      if (oldCapacity != list.Capacity) OnPropertyChanged("Capacity");
234      OnPropertyChanged("Item[]");
235      if (oldItems.Count != items.Count) OnPropertyChanged("Count");
236    }
237
[2572]238    public bool Remove(T item) {
239      int index = list.IndexOf(item);
240      if (index != -1) {
241        list.RemoveAt(index);
[8610]242        OnItemsRemoved(new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
243        OnItemsRemoved(new T[] { item });
[2620]244        OnPropertyChanged("Item[]");
245        OnPropertyChanged("Count");
[2572]246        return true;
247      }
248      return false;
249    }
250    public int RemoveAll(Predicate<T> match) {
[2573]251      if (match == null) throw new ArgumentNullException();
[2623]252      List<IndexedItem<T>> indexedItems = new List<IndexedItem<T>>();
253      List<T> items = new List<T>();
[2572]254      for (int i = 0; i < list.Count; i++) {
[2623]255        if (match(list[i])) {
256          indexedItems.Add(new IndexedItem<T>(i, list[i]));
257          items.Add(list[i]);
258        }
[2572]259      }
[2620]260      int result = 0;
[2623]261      if (indexedItems.Count > 0) {
[2620]262        result = list.RemoveAll(match);
[8610]263        OnItemsRemoved(indexedItems);
264        OnItemsRemoved(items);
[2620]265        OnPropertyChanged("Item[]");
266        OnPropertyChanged("Count");
267      }
[2572]268      return result;
269    }
270    public void RemoveAt(int index) {
271      T item = list[index];
272      list.RemoveAt(index);
[8610]273      OnItemsRemoved(new IndexedItem<T>[] { new IndexedItem<T>(index, item) });
274      OnItemsRemoved(new T[] { item });
[2620]275      OnPropertyChanged("Item[]");
276      OnPropertyChanged("Count");
[2572]277    }
278    public void RemoveRange(int index, int count) {
[2620]279      if (count > 0) {
[2623]280        IndexedItem<T>[] indexedItems = GetIndexedItems(index, count);
281        T[] items = new T[count];
282        list.CopyTo(index, items, 0, count);
[2620]283        list.RemoveRange(index, count);
[8610]284        OnItemsRemoved(indexedItems);
285        OnItemsRemoved(items);
[2620]286        OnPropertyChanged("Item[]");
287        OnPropertyChanged("Count");
288      }
[2572]289    }
290
291    public void Clear() {
[2620]292      if (list.Count > 0) {
[2623]293        IndexedItem<T>[] indexedItems = GetIndexedItems();
294        T[] items = list.ToArray();
[2620]295        list.Clear();
[8610]296        OnCollectionReset(new IndexedItem<T>[0], indexedItems);
297        OnCollectionReset(new T[0], items);
[2620]298        OnPropertyChanged("Item[]");
299        OnPropertyChanged("Count");
300      }
[2572]301    }
302
303    public void Reverse() {
[2620]304      if (list.Count > 1) {
305        IndexedItem<T>[] oldItems = GetIndexedItems();
306        list.Reverse();
[8610]307        OnItemsMoved(GetIndexedItems(), oldItems);
[2620]308        OnPropertyChanged("Item[]");
309      }
[2572]310    }
311    public void Reverse(int index, int count) {
[2620]312      if (count > 1) {
313        IndexedItem<T>[] oldItems = GetIndexedItems(index, count);
314        list.Reverse(index, count);
[8610]315        OnItemsMoved(GetIndexedItems(index, count), oldItems);
[2620]316        OnPropertyChanged("Item[]");
317      }
[2572]318    }
319
320    public void Sort() {
[2620]321      if (list.Count > 1) {
322        IndexedItem<T>[] oldItems = GetIndexedItems();
[10324]323        list.StableSort();
[8610]324        OnItemsMoved(GetIndexedItems(), oldItems);
[2620]325        OnPropertyChanged("Item[]");
326      }
[2572]327    }
328    public void Sort(Comparison<T> comparison) {
[2620]329      if (list.Count > 1) {
330        IndexedItem<T>[] oldItems = GetIndexedItems();
[10324]331        list.StableSort(comparison);
[8610]332        OnItemsMoved(GetIndexedItems(), oldItems);
[2620]333        OnPropertyChanged("Item[]");
334      }
[2572]335    }
336    public void Sort(IComparer<T> comparer) {
[2620]337      if (list.Count > 1) {
338        IndexedItem<T>[] oldItems = GetIndexedItems();
[10324]339        list.StableSort(comparer);
[8610]340        OnItemsMoved(GetIndexedItems(), oldItems);
[2620]341        OnPropertyChanged("Item[]");
342      }
[2572]343    }
344    public void Sort(int index, int count, IComparer<T> comparer) {
[2745]345      if (count > 1) {
[2620]346        IndexedItem<T>[] oldItems = GetIndexedItems(index, count);
[10324]347        list.StableSort(index, count, comparer);
[8610]348        OnItemsMoved(GetIndexedItems(index, count), oldItems);
[2620]349        OnPropertyChanged("Item[]");
350      }
[2572]351    }
352    #endregion
353
354    #region Conversion
[2618]355    public ReadOnlyObservableList<T> AsReadOnly() {
356      return new ReadOnlyObservableList<T>(this);
[2572]357    }
[8610]358
[2572]359    public T[] ToArray() {
360      return list.ToArray();
361    }
[2623]362    public void CopyTo(T[] array) {
363      list.CopyTo(array);
364    }
365    public void CopyTo(T[] array, int arrayIndex) {
[2572]366      list.CopyTo(array, arrayIndex);
367    }
[2623]368    public void CopyTo(int index, T[] array, int arrayIndex, int count) {
369      list.CopyTo(index, array, arrayIndex, count);
370    }
[2572]371    public List<TOutput> ConvertAll<TOutput>(Converter<T, TOutput> converter) {
372      return list.ConvertAll<TOutput>(converter);
373    }
374    #endregion
375
376    #region Processing
377    public void ForEach(Action<T> action) {
378      list.ForEach(action);
379    }
380    public bool TrueForAll(Predicate<T> match) {
381      return list.TrueForAll(match);
382    }
383    #endregion
384
385    #region Enumeration
[2623]386    public IEnumerator<T> GetEnumerator() {
[2572]387      return list.GetEnumerator();
388    }
389    IEnumerator IEnumerable.GetEnumerator() {
[2623]390      return list.GetEnumerator();
[2572]391    }
392    #endregion
393
394    #region Helpers
395    public void TrimExcess() {
[2620]396      int capacity = list.Capacity;
[2572]397      list.TrimExcess();
[2620]398      if (list.Capacity != capacity)
399        OnPropertyChanged("Capacity");
[2572]400    }
401    #endregion
402
[2574]403    #region Events
404    [field: NonSerialized]
405    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsAdded;
406    protected virtual void OnItemsAdded(IEnumerable<IndexedItem<T>> items) {
[3317]407      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = ItemsAdded;
408      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items));
[2574]409    }
410
411    [field: NonSerialized]
[2623]412    private event CollectionItemsChangedEventHandler<T> itemsAdded;
[2832]413    event CollectionItemsChangedEventHandler<T> INotifyObservableCollectionItemsChanged<T>.ItemsAdded {
[2623]414      add { itemsAdded += value; }
415      remove { itemsAdded -= value; }
416    }
417    private void OnItemsAdded(IEnumerable<T> items) {
[3317]418      CollectionItemsChangedEventHandler<T> handler = itemsAdded;
419      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<T>(items));
[2623]420    }
421
422    [field: NonSerialized]
[2574]423    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsRemoved;
424    protected virtual void OnItemsRemoved(IEnumerable<IndexedItem<T>> items) {
[3317]425      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = ItemsRemoved;
426      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items));
[2574]427    }
428
429    [field: NonSerialized]
[2623]430    private event CollectionItemsChangedEventHandler<T> itemsRemoved;
[2832]431    event CollectionItemsChangedEventHandler<T> INotifyObservableCollectionItemsChanged<T>.ItemsRemoved {
[2623]432      add { itemsRemoved += value; }
433      remove { itemsRemoved -= value; }
434    }
435    private void OnItemsRemoved(IEnumerable<T> items) {
[3317]436      CollectionItemsChangedEventHandler<T> handler = itemsRemoved;
437      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<T>(items));
[2623]438    }
439
440    [field: NonSerialized]
[2574]441    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsReplaced;
442    protected virtual void OnItemsReplaced(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
[3317]443      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = ItemsReplaced;
444      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
[2574]445    }
446
447    [field: NonSerialized]
448    public event CollectionItemsChangedEventHandler<IndexedItem<T>> ItemsMoved;
449    protected virtual void OnItemsMoved(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
[3317]450      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = ItemsMoved;
451      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
[2574]452    }
453
454    [field: NonSerialized]
455    public event CollectionItemsChangedEventHandler<IndexedItem<T>> CollectionReset;
456    protected virtual void OnCollectionReset(IEnumerable<IndexedItem<T>> items, IEnumerable<IndexedItem<T>> oldItems) {
[3317]457      CollectionItemsChangedEventHandler<IndexedItem<T>> handler = CollectionReset;
458      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<IndexedItem<T>>(items, oldItems));
[2574]459    }
[2620]460
461    [field: NonSerialized]
[2623]462    private event CollectionItemsChangedEventHandler<T> collectionReset;
[2832]463    event CollectionItemsChangedEventHandler<T> INotifyObservableCollectionItemsChanged<T>.CollectionReset {
[2623]464      add { collectionReset += value; }
465      remove { collectionReset -= value; }
466    }
467    private void OnCollectionReset(IEnumerable<T> items, IEnumerable<T> oldItems) {
[3317]468      CollectionItemsChangedEventHandler<T> handler = collectionReset;
469      if (handler != null) handler(this, new CollectionItemsChangedEventArgs<T>(items, oldItems));
[2623]470    }
471
472    [field: NonSerialized]
[2620]473    public event PropertyChangedEventHandler PropertyChanged;
474    protected virtual void OnPropertyChanged(string propertyName) {
[3317]475      PropertyChangedEventHandler handler = PropertyChanged;
476      if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
[2620]477    }
[2574]478    #endregion
479
[2572]480    #region Private helpers
481    private IndexedItem<T>[] GetIndexedItems() {
482      IndexedItem<T>[] items = new IndexedItem<T>[list.Count];
483      for (int i = 0; i < list.Count; i++)
484        items[i] = new IndexedItem<T>(i, list[i]);
485      return items;
486    }
487    private IndexedItem<T>[] GetIndexedItems(int index, int count) {
488      IndexedItem<T>[] items = new IndexedItem<T>[count];
489      for (int i = 0; i < count; i++)
490        items[i] = new IndexedItem<T>(index + i, list[index + i]);
491      return items;
492    }
493    #endregion
494  }
495}
Note: See TracBrowser for help on using the repository browser.