#region License Information /* HeuristicLab * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL) * * This file is part of HeuristicLab. * * HeuristicLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * HeuristicLab is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HeuristicLab. If not, see . */ #endregion using HEAL.Attic; using HeuristicLab.Common; using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace HeuristicLab.Collections { [StorableType("EBD47A4A-848B-487C-B996-2CA2A509A488")] [Serializable] public class ObservableList : IObservableList { [Storable] protected List list; #region Properties public int Capacity { get { return list.Capacity; } set { if (list.Capacity != value) { list.Capacity = value; OnPropertyChanged("Capacity"); } } } public int Count { get { return list.Count; } } bool ICollection.IsReadOnly { get { return ((ICollection)list).IsReadOnly; } } public T this[int index] { get { return list[index]; } set { T item = list[index]; if (!((item == null) && (value == null)) && ((item == null) || (!item.Equals(value)))) { list[index] = value; OnItemsReplaced(new IndexedItem[] { new IndexedItem(index, value) }, new IndexedItem[] { new IndexedItem(index, item) }); OnPropertyChanged("Item[]"); } } } #endregion #region Constructors public ObservableList() { list = new List(); } public ObservableList(int capacity) { list = new List(capacity); } public ObservableList(IEnumerable collection) { list = new List(collection); } [StorableConstructor] protected ObservableList(StorableConstructorFlag _) { } #endregion #region Access public List GetRange(int index, int count) { return list.GetRange(index, count); } public bool Contains(T item) { return list.Contains(item); } public int IndexOf(T item) { return list.IndexOf(item); } public int IndexOf(T item, int index) { return list.IndexOf(item, index); } public int IndexOf(T item, int index, int count) { return list.IndexOf(item, index, count); } public int LastIndexOf(T item) { return list.LastIndexOf(item); } public int LastIndexOf(T item, int index) { return list.LastIndexOf(item, index); } public int LastIndexOf(T item, int index, int count) { return list.LastIndexOf(item, index, count); } public int BinarySearch(T item) { return list.BinarySearch(item); } public int BinarySearch(T item, IComparer comparer) { return list.BinarySearch(item, comparer); } public int BinarySearch(int index, int count, T item, IComparer comparer) { return list.BinarySearch(index, count, item, comparer); } public bool Exists(Predicate match) { return list.Exists(match); } public T Find(Predicate match) { return list.Find(match); } public List FindAll(Predicate match) { return list.FindAll(match); } public T FindLast(Predicate match) { return list.FindLast(match); } public int FindIndex(Predicate match) { return list.FindIndex(match); } public int FindIndex(int startIndex, Predicate match) { return list.FindIndex(startIndex, match); } public int FindIndex(int startIndex, int count, Predicate match) { return list.FindIndex(startIndex, count, match); } public int FindLastIndex(Predicate match) { return list.FindLastIndex(match); } public int FindLastIndex(int startIndex, Predicate match) { return list.FindLastIndex(startIndex, match); } public int FindLastIndex(int startIndex, int count, Predicate match) { return list.FindLastIndex(startIndex, count, match); } #endregion #region Manipulation public void Add(T item) { int capacity = list.Capacity; list.Add(item); OnItemsAdded(new IndexedItem[] { new IndexedItem(list.Count - 1, item) }); OnItemsAdded(new T[] { item }); if (list.Capacity != capacity) OnPropertyChanged("Capacity"); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); } public void AddRange(IEnumerable collection) { int capacity = list.Capacity; int index = list.Count; List> items = new List>(); foreach (T item in collection) { items.Add(new IndexedItem(index, item)); index++; } list.AddRange(items.Select(x => x.Value)); if (items.Count > 0) { OnItemsAdded(items); OnItemsAdded(items.Select(x => x.Value)); if (list.Capacity != capacity) OnPropertyChanged("Capacity"); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); } } public void Insert(int index, T item) { int capacity = list.Capacity; list.Insert(index, item); OnItemsAdded(new IndexedItem[] { new IndexedItem(index, item) }); OnItemsAdded(new T[] { item }); if (list.Capacity != capacity) OnPropertyChanged("Capacity"); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); } public void InsertRange(int index, IEnumerable collection) { int capacity = list.Capacity; list.InsertRange(index, collection); List> items = new List>(); foreach (T item in collection) { items.Add(new IndexedItem(index, item)); index++; } if (items.Count > 0) { OnItemsAdded(items); OnItemsAdded(collection); if (list.Capacity != capacity) OnPropertyChanged("Capacity"); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); } } /// /// Performs a Clear and an AddRange, but does not fire separate events for those operations /// /// public void Replace(IEnumerable collection) { List> oldItems = null; if (list.Any()) oldItems = list.Select((x, i) => new IndexedItem(i, x)).ToList(); else oldItems = new List>(); int oldCapacity = list.Capacity; list.Clear(); list.AddRange(collection); List> items = null; if (list.Any()) items = list.Select((x, i) => new IndexedItem(i, x)).ToList(); else items = new List>(); OnItemsReplaced(items, oldItems); if (oldCapacity != list.Capacity) OnPropertyChanged("Capacity"); OnPropertyChanged("Item[]"); if (oldItems.Count != items.Count) OnPropertyChanged("Count"); } public bool Remove(T item) { int index = list.IndexOf(item); if (index != -1) { list.RemoveAt(index); OnItemsRemoved(new IndexedItem[] { new IndexedItem(index, item) }); OnItemsRemoved(new T[] { item }); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); return true; } return false; } public int RemoveAll(Predicate match) { if (match == null) throw new ArgumentNullException(); List> indexedItems = new List>(); List items = new List(); for (int i = 0; i < list.Count; i++) { if (match(list[i])) { indexedItems.Add(new IndexedItem(i, list[i])); items.Add(list[i]); } } int result = 0; if (indexedItems.Count > 0) { result = list.RemoveAll(match); OnItemsRemoved(indexedItems); OnItemsRemoved(items); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); } return result; } public void RemoveAt(int index) { T item = list[index]; list.RemoveAt(index); OnItemsRemoved(new IndexedItem[] { new IndexedItem(index, item) }); OnItemsRemoved(new T[] { item }); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); } public void RemoveRange(int index, int count) { if (count > 0) { IndexedItem[] indexedItems = GetIndexedItems(index, count); T[] items = new T[count]; list.CopyTo(index, items, 0, count); list.RemoveRange(index, count); OnItemsRemoved(indexedItems); OnItemsRemoved(items); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); } } public void Clear() { if (list.Count > 0) { IndexedItem[] indexedItems = GetIndexedItems(); T[] items = list.ToArray(); list.Clear(); OnCollectionReset(new IndexedItem[0], indexedItems); OnCollectionReset(new T[0], items); OnPropertyChanged("Item[]"); OnPropertyChanged("Count"); } } public void Reverse() { if (list.Count > 1) { IndexedItem[] oldItems = GetIndexedItems(); list.Reverse(); OnItemsMoved(GetIndexedItems(), oldItems); OnPropertyChanged("Item[]"); } } public void Reverse(int index, int count) { if (count > 1) { IndexedItem[] oldItems = GetIndexedItems(index, count); list.Reverse(index, count); OnItemsMoved(GetIndexedItems(index, count), oldItems); OnPropertyChanged("Item[]"); } } public void Sort() { if (list.Count > 1) { IndexedItem[] oldItems = GetIndexedItems(); list.StableSort(); OnItemsMoved(GetIndexedItems(), oldItems); OnPropertyChanged("Item[]"); } } public void Sort(Comparison comparison) { if (list.Count > 1) { IndexedItem[] oldItems = GetIndexedItems(); list.StableSort(comparison); OnItemsMoved(GetIndexedItems(), oldItems); OnPropertyChanged("Item[]"); } } public void Sort(IComparer comparer) { if (list.Count > 1) { IndexedItem[] oldItems = GetIndexedItems(); list.StableSort(comparer); OnItemsMoved(GetIndexedItems(), oldItems); OnPropertyChanged("Item[]"); } } public void Sort(int index, int count, IComparer comparer) { if (count > 1) { IndexedItem[] oldItems = GetIndexedItems(index, count); list.StableSort(index, count, comparer); OnItemsMoved(GetIndexedItems(index, count), oldItems); OnPropertyChanged("Item[]"); } } #endregion #region Conversion public ReadOnlyObservableList AsReadOnly() { return new ReadOnlyObservableList(this); } public T[] ToArray() { return list.ToArray(); } public void CopyTo(T[] array) { list.CopyTo(array); } public void CopyTo(T[] array, int arrayIndex) { list.CopyTo(array, arrayIndex); } public void CopyTo(int index, T[] array, int arrayIndex, int count) { list.CopyTo(index, array, arrayIndex, count); } public List ConvertAll(Converter converter) { return list.ConvertAll(converter); } #endregion #region Processing public void ForEach(Action action) { list.ForEach(action); } public bool TrueForAll(Predicate match) { return list.TrueForAll(match); } #endregion #region Enumeration public IEnumerator GetEnumerator() { return list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return list.GetEnumerator(); } #endregion #region Helpers public void TrimExcess() { int capacity = list.Capacity; list.TrimExcess(); if (list.Capacity != capacity) OnPropertyChanged("Capacity"); } #endregion #region Events [field: NonSerialized] public event CollectionItemsChangedEventHandler> ItemsAdded; protected virtual void OnItemsAdded(IEnumerable> items) { CollectionItemsChangedEventHandler> handler = ItemsAdded; if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items)); } [field: NonSerialized] private event CollectionItemsChangedEventHandler itemsAdded; event CollectionItemsChangedEventHandler INotifyObservableCollectionItemsChanged.ItemsAdded { add { itemsAdded += value; } remove { itemsAdded -= value; } } private void OnItemsAdded(IEnumerable items) { CollectionItemsChangedEventHandler handler = itemsAdded; if (handler != null) handler(this, new CollectionItemsChangedEventArgs(items)); } [field: NonSerialized] public event CollectionItemsChangedEventHandler> ItemsRemoved; protected virtual void OnItemsRemoved(IEnumerable> items) { CollectionItemsChangedEventHandler> handler = ItemsRemoved; if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items)); } [field: NonSerialized] private event CollectionItemsChangedEventHandler itemsRemoved; event CollectionItemsChangedEventHandler INotifyObservableCollectionItemsChanged.ItemsRemoved { add { itemsRemoved += value; } remove { itemsRemoved -= value; } } private void OnItemsRemoved(IEnumerable items) { CollectionItemsChangedEventHandler handler = itemsRemoved; if (handler != null) handler(this, new CollectionItemsChangedEventArgs(items)); } [field: NonSerialized] public event CollectionItemsChangedEventHandler> ItemsReplaced; protected virtual void OnItemsReplaced(IEnumerable> items, IEnumerable> oldItems) { CollectionItemsChangedEventHandler> handler = ItemsReplaced; if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items, oldItems)); } [field: NonSerialized] public event CollectionItemsChangedEventHandler> ItemsMoved; protected virtual void OnItemsMoved(IEnumerable> items, IEnumerable> oldItems) { CollectionItemsChangedEventHandler> handler = ItemsMoved; if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items, oldItems)); } [field: NonSerialized] public event CollectionItemsChangedEventHandler> CollectionReset; protected virtual void OnCollectionReset(IEnumerable> items, IEnumerable> oldItems) { CollectionItemsChangedEventHandler> handler = CollectionReset; if (handler != null) handler(this, new CollectionItemsChangedEventArgs>(items, oldItems)); } [field: NonSerialized] private event CollectionItemsChangedEventHandler collectionReset; event CollectionItemsChangedEventHandler INotifyObservableCollectionItemsChanged.CollectionReset { add { collectionReset += value; } remove { collectionReset -= value; } } private void OnCollectionReset(IEnumerable items, IEnumerable oldItems) { CollectionItemsChangedEventHandler handler = collectionReset; if (handler != null) handler(this, new CollectionItemsChangedEventArgs(items, oldItems)); } [field: NonSerialized] public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); } #endregion #region Private helpers private IndexedItem[] GetIndexedItems() { IndexedItem[] items = new IndexedItem[list.Count]; for (int i = 0; i < list.Count; i++) items[i] = new IndexedItem(i, list[i]); return items; } private IndexedItem[] GetIndexedItems(int index, int count) { IndexedItem[] items = new IndexedItem[count]; for (int i = 0; i < count; i++) items[i] = new IndexedItem(index + i, list[index + i]); return items; } #endregion } }