using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; /* Turn off documentation warnings in this file. Generally they are * relevant in this library, but not for this file since it is mostly * methods found in normal List<> class, and thus it is no need to write * that comments once again with the drawback of making it hard to read. */ #pragma warning disable 1591 namespace Wayne.Lib { /// <summary> /// Extends the IList interface with some more members found in List class. /// </summary> /// <typeparam name="T"></typeparam> public interface IAdvancedList<T> : IList<T> { void AddRange(IEnumerable<T> collection); ReadOnlyCollection<T> AsReadOnly(); void CopyTo(T[] array); void CopyTo(int index, T[] array, int arrayIndex, int count); void BeginUpdate(); void EndUpdate(); } /// <summary> /// Extends IAdvanced List with an event handler that makes it possible to /// follow when the contents has benn changed. /// </summary> /// <typeparam name="T"></typeparam> public interface IObservableList<T> : IAdvancedList<T> { event EventHandler Changed; } /// <summary> /// Observable list is a list class similar to .Net's List but with an /// event that notifies when something has been changed in the list. /// </summary> /// <typeparam name="T"></typeparam> public class ObservableList<T> : IObservableList<T>, IList { private object syncRoot; private readonly List<T> internalList = new List<T>(); private int updateCount; private bool updated; public IEnumerator<T> GetEnumerator() { return internalList.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public int Count { get { return internalList.Count; } } int ICollection.Count { get { return Count; } } public bool Contains(T item) { return internalList.Contains(item); } public bool Contains(object value) { return IsCompatibleObject(value) && Contains((T)value); } public void Clear() { if (internalList.Count > 0) { internalList.Clear(); ListChanged(); } } void IList.Clear() { Clear(); } public int IndexOf(T item) { return internalList.IndexOf(item); } int IList.IndexOf(object value) { return IsCompatibleObject(value) ? IndexOf((T)value) : -1; } public T this[int index] { get { return internalList[index]; } set { internalList[index] = value; ListChanged(); } } object IList.this[int index] { get { return internalList[index]; } set { VerifyValueType(value); this[index] = (T)value; } } public void Add(T item) { internalList.Add(item); ListChanged(); } public int Add(object value) { VerifyValueType(value); Add((T)value); return Count - 1; } public void AddRange(IEnumerable<T> collection) { internalList.AddRange(collection); ListChanged(); } public ReadOnlyCollection<T> AsReadOnly() { return new ReadOnlyCollection<T>(internalList); } public void Insert(int index, T item) { internalList.Insert(index, item); ListChanged(); } public void Insert(int index, object value) { VerifyValueType(value); Insert(index, (T)value); } public bool Remove(T item) { if (internalList.Remove(item)) { ListChanged(); return true; } return false; } public void Remove(object value) { VerifyValueType(value); Remove((T)value); } public void RemoveAt(int index) { internalList.RemoveAt(index); ListChanged(); } void IList.RemoveAt(int index) { RemoveAt(index); } bool IList.IsReadOnly { get { return false; } } public bool IsFixedSize { get { return false; } } public void CopyTo(T[] array) { internalList.CopyTo(array); } public void CopyTo(T[] array, int arrayIndex) { internalList.CopyTo(array, arrayIndex); } public void CopyTo(int index, T[] array, int arrayIndex, int count) { internalList.CopyTo(index, array, arrayIndex, count); } public void CopyTo(Array array, int index) { if ((array != null) && (array.Rank != 1)) throw new ArgumentException("Only single dimensional arrays are supported for the requested action."); try { Array.Copy(internalList.ToArray(), 0, array, index, internalList.Count); } catch (ArrayTypeMismatchException) { throw new ArgumentException("Target array type is not compatible with the type of items in the collection."); } } public object SyncRoot { get { if (syncRoot == null) System.Threading.Interlocked.CompareExchange(ref syncRoot, new object(), null); return syncRoot; } } public bool IsSynchronized { get { return false; } } public bool IsReadOnly { get { return IsReadOnly; } } private static bool IsCompatibleObject(object value) { return (value is T) || (value == null && !typeof(T).IsValueType); } private static void VerifyValueType(object value) { if (!IsCompatibleObject(value)) throw new ArgumentException(string.Concat("The value \"", value.GetType(), "\" is not of type \"", typeof(T), "\" and cannot be used in this generic collection.")); } public event EventHandler Changed; public void BeginUpdate() { updateCount++; } public void EndUpdate() { updateCount--; if ((updateCount == 0) && updated) { updated = false; ListChanged(); } } private void ListChanged() { if (updateCount > 0) { updated = true; return; } if (Changed != null) Changed(this, EventArgs.Empty); } } }