diff --git a/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollection.cs b/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollection.cs new file mode 100644 --- /dev/null +++ b/GMap.NET.WindowsForms/GMap.NET.ObjectModel/ObservableCollection.cs @@ -0,0 +1,541 @@ +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); + } + } + } + } +}