using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; namespace GMap.NET.ObjectModel { public delegate void NotifyCollectionChangedEventHandler(object sender, NotifyCollectionChangedEventArgs e); public interface INotifyCollectionChanged { // Events event NotifyCollectionChangedEventHandler CollectionChanged; } public interface INotifyPropertyChanged { // Events event PropertyChangedEventHandler PropertyChanged; } public enum NotifyCollectionChangedAction { Add, Remove, Replace, Move, Reset } public class NotifyCollectionChangedEventArgs : EventArgs { // Fields private NotifyCollectionChangedAction _action; private IList _newItems; private int _newStartingIndex; private IList _oldItems; private int _oldStartingIndex; // Methods public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(action != NotifyCollectionChangedAction.Reset) { throw new ArgumentException("WrongActionForCtor", "action"); } this.InitializeAdd(action, null, -1); } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList changedItems) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove)) && (action != NotifyCollectionChangedAction.Reset)) { throw new ArgumentException("MustBeResetAddOrRemoveActionForCtor", "action"); } if(action == NotifyCollectionChangedAction.Reset) { if(changedItems != null) { throw new ArgumentException("ResetActionRequiresNullItem", "action"); } this.InitializeAdd(action, null, -1); } else { if(changedItems == null) { throw new ArgumentNullException("changedItems"); } this.InitializeAddOrRemove(action, changedItems, -1); } } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object changedItem) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove)) && (action != NotifyCollectionChangedAction.Reset)) { throw new ArgumentException("MustBeResetAddOrRemoveActionForCtor", "action"); } if(action == NotifyCollectionChangedAction.Reset) { if(changedItem != null) { throw new ArgumentException("ResetActionRequiresNullItem", "action"); } this.InitializeAdd(action, null, -1); } else { this.InitializeAddOrRemove(action, new object[] { changedItem }, -1); } } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList newItems, IList oldItems) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(action != NotifyCollectionChangedAction.Replace) { throw new ArgumentException("WrongActionForCtor", "action"); } if(newItems == null) { throw new ArgumentNullException("newItems"); } if(oldItems == null) { throw new ArgumentNullException("oldItems"); } this.InitializeMoveOrReplace(action, newItems, oldItems, -1, -1); } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList changedItems, int startingIndex) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove)) && (action != NotifyCollectionChangedAction.Reset)) { throw new ArgumentException("MustBeResetAddOrRemoveActionForCtor", "action"); } if(action == NotifyCollectionChangedAction.Reset) { if(changedItems != null) { throw new ArgumentException("ResetActionRequiresNullItem", "action"); } if(startingIndex != -1) { throw new ArgumentException("ResetActionRequiresIndexMinus1", "action"); } this.InitializeAdd(action, null, -1); } else { if(changedItems == null) { throw new ArgumentNullException("changedItems"); } if(startingIndex < -1) { throw new ArgumentException("IndexCannotBeNegative", "startingIndex"); } this.InitializeAddOrRemove(action, changedItems, startingIndex); } } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object changedItem, int index) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(((action != NotifyCollectionChangedAction.Add) && (action != NotifyCollectionChangedAction.Remove)) && (action != NotifyCollectionChangedAction.Reset)) { throw new ArgumentException("MustBeResetAddOrRemoveActionForCtor", "action"); } if(action == NotifyCollectionChangedAction.Reset) { if(changedItem != null) { throw new ArgumentException("ResetActionRequiresNullItem", "action"); } if(index != -1) { throw new ArgumentException("ResetActionRequiresIndexMinus1", "action"); } this.InitializeAdd(action, null, -1); } else { this.InitializeAddOrRemove(action, new object[] { changedItem }, index); } } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object newItem, object oldItem) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(action != NotifyCollectionChangedAction.Replace) { throw new ArgumentException("WrongActionForCtor", "action"); } this.InitializeMoveOrReplace(action, new object[] { newItem }, new object[] { oldItem }, -1, -1); } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList newItems, IList oldItems, int startingIndex) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(action != NotifyCollectionChangedAction.Replace) { throw new ArgumentException("WrongActionForCtor", "action"); } if(newItems == null) { throw new ArgumentNullException("newItems"); } if(oldItems == null) { throw new ArgumentNullException("oldItems"); } this.InitializeMoveOrReplace(action, newItems, oldItems, startingIndex, startingIndex); } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, IList changedItems, int index, int oldIndex) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(action != NotifyCollectionChangedAction.Move) { throw new ArgumentException("WrongActionForCtor", "action"); } if(index < 0) { throw new ArgumentException("IndexCannotBeNegative", "index"); } this.InitializeMoveOrReplace(action, changedItems, changedItems, index, oldIndex); } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object changedItem, int index, int oldIndex) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(action != NotifyCollectionChangedAction.Move) { throw new ArgumentException("WrongActionForCtor", "action"); } if(index < 0) { throw new ArgumentException("IndexCannotBeNegative", "index"); } object[] newItems = new object[] { changedItem }; this.InitializeMoveOrReplace(action, newItems, newItems, index, oldIndex); } public NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction action, object newItem, object oldItem, int index) { this._newStartingIndex = -1; this._oldStartingIndex = -1; if(action != NotifyCollectionChangedAction.Replace) { throw new ArgumentException("WrongActionForCtor", "action"); } this.InitializeMoveOrReplace(action, new object[] { newItem }, new object[] { oldItem }, index, index); } private void InitializeAdd(NotifyCollectionChangedAction action, IList newItems, int newStartingIndex) { this._action = action; #if !PocketPC this._newItems = (newItems == null) ? null : ArrayList.ReadOnly(newItems); #else this._newItems = (newItems == null) ? null : newItems; #endif this._newStartingIndex = newStartingIndex; } private void InitializeAddOrRemove(NotifyCollectionChangedAction action, IList changedItems, int startingIndex) { if(action == NotifyCollectionChangedAction.Add) { this.InitializeAdd(action, changedItems, startingIndex); } else if(action == NotifyCollectionChangedAction.Remove) { this.InitializeRemove(action, changedItems, startingIndex); } else { throw new ArgumentException(string.Format("InvariantFailure, Unsupported action: {0}", action.ToString())); } } private void InitializeMoveOrReplace(NotifyCollectionChangedAction action, IList newItems, IList oldItems, int startingIndex, int oldStartingIndex) { this.InitializeAdd(action, newItems, startingIndex); this.InitializeRemove(action, oldItems, oldStartingIndex); } private void InitializeRemove(NotifyCollectionChangedAction action, IList oldItems, int oldStartingIndex) { this._action = action; #if !PocketPC this._oldItems = (oldItems == null) ? null : ArrayList.ReadOnly(oldItems); #else this._oldItems = (oldItems == null) ? null : oldItems; #endif this._oldStartingIndex = oldStartingIndex; } // Properties public NotifyCollectionChangedAction Action { get { return this._action; } } public IList NewItems { get { return this._newItems; } } public int NewStartingIndex { get { return this._newStartingIndex; } } public IList OldItems { get { return this._oldItems; } } public int OldStartingIndex { get { return this._oldStartingIndex; } } } [Serializable] public class ObservableCollection : Collection, INotifyCollectionChanged, INotifyPropertyChanged { // Fields private SimpleMonitor _monitor; private const string CountString = "Count"; private const string IndexerName = "Item[]"; // Events [field: NonSerialized] public virtual event NotifyCollectionChangedEventHandler CollectionChanged; [field: NonSerialized] protected event PropertyChangedEventHandler PropertyChanged; event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged { add { PropertyChanged += value; } remove { PropertyChanged -= value; } } // Methods public ObservableCollection() { this._monitor = new SimpleMonitor(); } public ObservableCollection(IEnumerable collection) { this._monitor = new SimpleMonitor(); if(collection == null) { throw new ArgumentNullException("collection"); } this.CopyFrom(collection); } public ObservableCollection(List list) : base((list != null) ? new List(list.Count) : list) { this._monitor = new SimpleMonitor(); this.CopyFrom(list); } protected IDisposable BlockReentrancy() { this._monitor.Enter(); return this._monitor; } protected void CheckReentrancy() { if((this._monitor.Busy && (this.CollectionChanged != null)) && (this.CollectionChanged.GetInvocationList().Length > 1)) { throw new InvalidOperationException("ObservableCollectionReentrancyNotAllowed"); } } protected override void ClearItems() { this.CheckReentrancy(); base.ClearItems(); this.OnPropertyChanged(CountString); this.OnPropertyChanged(IndexerName); this.OnCollectionReset(); } private void CopyFrom(IEnumerable collection) { IList items = base.Items; if((collection != null) && (items != null)) { using(IEnumerator enumerator = collection.GetEnumerator()) { while(enumerator.MoveNext()) { items.Add(enumerator.Current); } } } } protected override void InsertItem(int index, T item) { this.CheckReentrancy(); base.InsertItem(index, item); this.OnPropertyChanged(CountString); this.OnPropertyChanged(IndexerName); this.OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index); } public void Move(int oldIndex, int newIndex) { this.MoveItem(oldIndex, newIndex); } protected virtual void MoveItem(int oldIndex, int newIndex) { this.CheckReentrancy(); T item = base[oldIndex]; base.RemoveItem(oldIndex); base.InsertItem(newIndex, item); this.OnPropertyChanged(IndexerName); this.OnCollectionChanged(NotifyCollectionChangedAction.Move, item, newIndex, oldIndex); } protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { if(this.CollectionChanged != null) { using(this.BlockReentrancy()) { this.CollectionChanged(this, e); } } } private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index)); } private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index, int oldIndex) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index, oldIndex)); } private void OnCollectionChanged(NotifyCollectionChangedAction action, object oldItem, object newItem, int index) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, newItem, oldItem, index)); } private void OnCollectionReset() { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) { if(this.PropertyChanged != null) { this.PropertyChanged(this, e); } } private void OnPropertyChanged(string propertyName) { this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } protected override void RemoveItem(int index) { this.CheckReentrancy(); T item = base[index]; base.RemoveItem(index); this.OnPropertyChanged(CountString); this.OnPropertyChanged(IndexerName); this.OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index); } protected override void SetItem(int index, T item) { this.CheckReentrancy(); T oldItem = base[index]; base.SetItem(index, item); this.OnPropertyChanged(IndexerName); this.OnCollectionChanged(NotifyCollectionChangedAction.Replace, oldItem, item, index); } // Nested Types [Serializable] private class SimpleMonitor : IDisposable { // Fields private int _busyCount; // Methods public void Dispose() { this._busyCount--; } public void Enter() { this._busyCount++; } // Properties public bool Busy { get { return (this._busyCount > 0); } } } } }