#region CPL License /* Nuclex Framework Copyright (C) 2002-2017 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library 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 IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion using System; using System.Collections; using System.Collections.Generic; #if !NO_SPECIALIZED_COLLECTIONS using System.Collections.Specialized; #endif namespace Nuclex.Support.Collections { /// List which fires events when items are added or removed /// Type of items the collection manages public class ObservableList : IList, IList, ICollection, #if !NO_SPECIALIZED_COLLECTIONS INotifyCollectionChanged, #endif IObservableCollection { /// Raised when an item has been added to the collection public event EventHandler> ItemAdded; /// Raised when an item is removed from the collection public event EventHandler> ItemRemoved; /// Raised when an item is replaced in the collection public event EventHandler> ItemReplaced; /// Raised when the collection is about to be cleared /// /// This could be covered by calling ItemRemoved for each item currently /// contained in the collection, but it is often simpler and more efficient /// to process the clearing of the entire collection as a special operation. /// public event EventHandler Clearing; /// Raised when the collection has been cleared public event EventHandler Cleared; #if !NO_SPECIALIZED_COLLECTIONS /// Called when the collection has changed public event NotifyCollectionChangedEventHandler CollectionChanged; #endif /// /// Initializes a new instance of the ObservableList class that is empty. /// public ObservableList() : this(new List()) { } /// /// Initializes a new instance of the ObservableList class as a wrapper /// for the specified list. /// /// The list that is wrapped by the new collection. /// List is null public ObservableList(IList list) { this.typedList = list; this.objectList = list as IList; // Gah! } /// Determines the index of the specified item in the list /// Item whose index will be determined /// The index of the item in the list or -1 if not found public int IndexOf(TItem item) { return this.typedList.IndexOf(item); } /// Inserts an item into the list at the specified index /// Index the item will be insertted at /// Item that will be inserted into the list public void Insert(int index, TItem item) { this.typedList.Insert(index, item); OnAdded(item, index); } /// Removes the item at the specified index from the list /// Index at which the item will be removed public void RemoveAt(int index) { TItem item = this.typedList[index]; this.typedList.RemoveAt(index); OnRemoved(item, index); } /// Accesses the item at the specified index in the list /// Index of the item that will be accessed /// The item at the specified index public TItem this[int index] { get { return this.typedList[index]; } set { TItem oldItem = this.typedList[index]; this.typedList[index] = value; OnReplaced(oldItem, value, index); } } /// Adds an item to the end of the list /// Item that will be added to the list public void Add(TItem item) { this.typedList.Add(item); OnAdded(item, this.typedList.Count - 1); } /// Removes all items from the list public void Clear() { OnClearing(); this.typedList.Clear(); OnCleared(); } /// Checks whether the list contains the specified item /// Item the list will be checked for /// True if the list contains the specified items public bool Contains(TItem item) { return this.typedList.Contains(item); } /// Copies the contents of the list into an array /// Array the list will be copied into /// /// Index in the target array where the first item will be copied to /// public void CopyTo(TItem[] array, int arrayIndex) { this.typedList.CopyTo(array, arrayIndex); } /// Total number of items in the list public int Count { get { return this.typedList.Count; } } /// Whether the list is a read-only list public bool IsReadOnly { get { return this.typedList.IsReadOnly; } } /// Removes the specified item from the list /// Item that will be removed from the list /// /// True if the item was found and removed from the list, false otherwise /// public bool Remove(TItem item) { int index = this.typedList.IndexOf(item); if(index == -1) { return false; } TItem removedItem = this.typedList[index]; this.typedList.RemoveAt(index); OnRemoved(removedItem, index); return true; } /// Returns an enumerator for the items in the list /// An enumerator for the list's items public IEnumerator GetEnumerator() { return this.typedList.GetEnumerator(); } #region IEnumerable implementation /// Returns an enumerator for the items in the list /// An enumerator for the list's items IEnumerator IEnumerable.GetEnumerator() { return this.objectList.GetEnumerator(); } #endregion // IEnumerable implementation #region ICollection implementation /// Copies the contents of the list into an array /// Array the list will be copied into /// /// Index in the target array where the first item will be copied to /// void ICollection.CopyTo(Array array, int arrayIndex) { this.objectList.CopyTo(array, arrayIndex); } /// Whether this list performs thread synchronization bool ICollection.IsSynchronized { get { return this.objectList.IsSynchronized; } } /// Synchronization root used by the list to synchronize threads object ICollection.SyncRoot { get { return this.objectList.SyncRoot; } } #endregion // ICollection implementation #region IList implementation /// Adds an item to the list /// Item that will be added to the list /// /// The position at which the item has been inserted or -1 if the item was not inserted /// int IList.Add(object value) { int index = this.objectList.Add(value); TItem addedItem = this.typedList[index]; OnAdded(addedItem, index); return index; } /// Checks whether the list contains the specified item /// Item the list will be checked for /// True if the list contains the specified items bool IList.Contains(object item) { return this.objectList.Contains(item); } /// Determines the index of the specified item in the list /// Item whose index will be determined /// The index of the item in the list or -1 if not found int IList.IndexOf(object item) { return this.objectList.IndexOf(item); } /// Inserts an item into the list at the specified index /// Index the item will be insertted at /// Item that will be inserted into the list void IList.Insert(int index, object item) { this.objectList.Insert(index, item); TItem addedItem = this.typedList[index]; OnAdded(addedItem, index); } /// Whether the list is of a fixed size bool IList.IsFixedSize { get { return this.objectList.IsFixedSize; } } /// Removes the specified item from the list /// Item that will be removed from the list void IList.Remove(object item) { int index = this.objectList.IndexOf(item); if(index == -1) { return; } TItem removedItem = this.typedList[index]; this.objectList.RemoveAt(index); OnRemoved(removedItem, index); } /// Accesses the item at the specified index in the list /// Index of the item that will be accessed /// The item at the specified index object IList.this[int index] { get { return this.objectList[index]; } set { TItem oldItem = this.typedList[index]; this.objectList[index] = value; TItem newItem = this.typedList[index]; OnReplaced(oldItem, newItem, index); } } #endregion // IList implementation /// Fires the 'ItemAdded' event /// Item that has been added to the collection /// Index of the added item protected virtual void OnAdded(TItem item, int index) { if(ItemAdded != null) { ItemAdded(this, new ItemEventArgs(item)); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index) ); } #endif } /// Fires the 'ItemRemoved' event /// Item that has been removed from the collection /// Index the item has been removed from protected virtual void OnRemoved(TItem item, int index) { if(ItemRemoved != null) { ItemRemoved(this, new ItemEventArgs(item)); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index) ); } #endif } /// Fires the 'ItemReplaced' event /// Item that has been replaced /// New item the original item was replaced with /// Index of the replaced item protected virtual void OnReplaced(TItem oldItem, TItem newItem, int index) { if(ItemReplaced != null) { ItemReplaced(this, new ItemReplaceEventArgs(oldItem, newItem)); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Replace, newItem, oldItem, index ) ); } #endif } /// Fires the 'Clearing' event protected virtual void OnClearing() { if(Clearing != null) { Clearing(this, EventArgs.Empty); } } /// Fires the 'Cleared' event protected virtual void OnCleared() { if(Cleared != null) { Cleared(this, EventArgs.Empty); } #if !NO_SPECIALIZED_COLLECTIONS if(CollectionChanged != null) { CollectionChanged(this, Constants.NotifyCollectionResetEventArgs); } #endif } /// The wrapped list under its type-safe interface private IList typedList; /// The wrapped list under its object interface private IList objectList; } } // namespace Nuclex.Support.Collections