diff --git a/Nuclex.Support (net-4.0).csproj b/Nuclex.Support (net-4.0).csproj index 98e931f..7057908 100644 --- a/Nuclex.Support (net-4.0).csproj +++ b/Nuclex.Support (net-4.0).csproj @@ -85,6 +85,10 @@ SerializationCloner.cs + + + Constants.cs + Deque.cs diff --git a/Nuclex.Support (xna-4.0-phone7).csproj b/Nuclex.Support (xna-4.0-phone7).csproj index 26cbdf0..67cf800 100644 --- a/Nuclex.Support (xna-4.0-phone7).csproj +++ b/Nuclex.Support (xna-4.0-phone7).csproj @@ -116,6 +116,10 @@ SerializationCloner.cs + + + Constants.cs + Deque.cs diff --git a/Nuclex.Support (xna-4.0-xbox360).csproj b/Nuclex.Support (xna-4.0-xbox360).csproj index 4eafe45..aa75ddc 100644 --- a/Nuclex.Support (xna-4.0-xbox360).csproj +++ b/Nuclex.Support (xna-4.0-xbox360).csproj @@ -127,6 +127,10 @@ SerializationCloner.cs + + + Constants.cs + Deque.cs diff --git a/Source/Collections/Constants.Test.cs b/Source/Collections/Constants.Test.cs new file mode 100644 index 0000000..e8bdbc5 --- /dev/null +++ b/Source/Collections/Constants.Test.cs @@ -0,0 +1,49 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2012 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 + +#if UNITTEST + +using System; +using System.Collections.Specialized; + +using NUnit.Framework; + +namespace Nuclex.Support.Collections { + + /// Unit Test for the collection constants + [TestFixture] + internal class ConstantsTest { + + /// + /// Verifies that the collection reset event arguments have 'reset' specified as + /// their action + /// + [Test] + public void CollectionResetEventArgsHaveResetActionSet() { + Assert.AreEqual( + NotifyCollectionChangedAction.Reset, Constants.NotifyCollectionResetEventArgs.Action + ); + } + + } + +} // namespace Nuclex.Support.Collections + +#endif // UNITTEST diff --git a/Source/Collections/Constants.cs b/Source/Collections/Constants.cs new file mode 100644 index 0000000..202a3ff --- /dev/null +++ b/Source/Collections/Constants.cs @@ -0,0 +1,35 @@ +#region CPL License +/* +Nuclex Framework +Copyright (C) 2002-2012 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.Specialized; + +namespace Nuclex.Support.Collections { + + /// Contains fixed constants used by some collections + public static class Constants { + + /// Fixed event args used to notify that the collection has reset + public static readonly NotifyCollectionChangedEventArgs NotifyCollectionResetEventArgs = + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); + + } + +} // namespace Nuclex.Support.Collections diff --git a/Source/Collections/IObservableCollection.cs b/Source/Collections/IObservableCollection.cs index d932511..48239e7 100644 --- a/Source/Collections/IObservableCollection.cs +++ b/Source/Collections/IObservableCollection.cs @@ -34,10 +34,8 @@ namespace Nuclex.Support.Collections { /// Raised when an item is removed from the collection event EventHandler> ItemRemoved; -#if false /// Raised when an item is replaced in the collection - event EventHandler> ItemReplaced; -#endif + event EventHandler> ItemReplaced; /// Raised when the collection is about to be cleared /// diff --git a/Source/Collections/ObservableCollection.cs b/Source/Collections/ObservableCollection.cs index fc65d06..037129e 100644 --- a/Source/Collections/ObservableCollection.cs +++ b/Source/Collections/ObservableCollection.cs @@ -34,14 +34,19 @@ namespace Nuclex.Support.Collections { ICollection, ICollection, #if !NO_SPECIALIZED_COLLECTIONS - INotifyCollectionChanged, + INotifyCollectionChanged, #endif - IObservableCollection { + 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 { + add { } + remove { } + } /// Raised when the collection is about to be cleared /// /// This could be covered by calling ItemRemoved for each item currently @@ -58,7 +63,7 @@ namespace Nuclex.Support.Collections { #endif /// Initializes a new ObservableCollection with no items - public ObservableCollection() : this(new Collection()) {} + public ObservableCollection() : this(new Collection()) { } /// /// Initializes a new ObservableCollection as a wrapper for an existing collection @@ -75,11 +80,6 @@ namespace Nuclex.Support.Collections { OnClearing(); this.typedCollection.Clear(); OnCleared(); -#if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) { - CollectionChanged(this, CollectionResetEventArgs); - } -#endif } /// Adds an item to the collection @@ -87,14 +87,6 @@ namespace Nuclex.Support.Collections { public void Add(TItem item) { this.typedCollection.Add(item); OnAdded(item); -#if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) { - CollectionChanged( - this, - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item) - ); - } -#endif } /// Determines whether the collection contains the specified item @@ -132,14 +124,6 @@ namespace Nuclex.Support.Collections { bool wasRemoved = this.typedCollection.Remove(item); if(wasRemoved) { OnRemoved(item); -#if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) { - CollectionChanged( - this, - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item) - ); - } -#endif } return wasRemoved; @@ -154,27 +138,52 @@ namespace Nuclex.Support.Collections { /// Fires the 'ItemAdded' event /// Item that has been added to the collection protected virtual void OnAdded(TItem item) { - if(ItemAdded != null) + if(ItemAdded != null) { ItemAdded(this, new ItemEventArgs(item)); + } +#if !NO_SPECIALIZED_COLLECTIONS + if(CollectionChanged != null) { + CollectionChanged( + this, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item) + ); + } +#endif } /// Fires the 'ItemRemoved' event /// Item that has been removed from the collection protected virtual void OnRemoved(TItem item) { - if(ItemRemoved != null) + if(ItemRemoved != null) { ItemRemoved(this, new ItemEventArgs(item)); + } +#if !NO_SPECIALIZED_COLLECTIONS + if(CollectionChanged != null) { + CollectionChanged( + this, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item) + ); + } +#endif } /// Fires the 'Clearing' event protected virtual void OnClearing() { - if(Clearing != null) + if(Clearing != null) { Clearing(this, EventArgs.Empty); + } } /// Fires the 'Cleared' event protected virtual void OnCleared() { - if(Cleared != null) + if(Cleared != null) { Cleared(this, EventArgs.Empty); + } +#if !NO_SPECIALIZED_COLLECTIONS + if(CollectionChanged != null) { + CollectionChanged(this, Constants.NotifyCollectionResetEventArgs); + } +#endif } #region IEnumerable implementation @@ -212,12 +221,6 @@ namespace Nuclex.Support.Collections { #endregion // IEnumerable implementation -#if !NO_SPECIALIZED_COLLECTIONS - /// Fixed event args used to notify that the collection has reset - private static readonly NotifyCollectionChangedEventArgs CollectionResetEventArgs = - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); -#endif - /// The wrapped collection under its type-safe interface private ICollection typedCollection; /// The wrapped collection under its object interface diff --git a/Source/Collections/ObservableDictionary.cs b/Source/Collections/ObservableDictionary.cs index 05d8cf0..d5274fe 100644 --- a/Source/Collections/ObservableDictionary.cs +++ b/Source/Collections/ObservableDictionary.cs @@ -36,15 +36,15 @@ namespace Nuclex.Support.Collections { #endif public class ObservableDictionary : #if !NO_SERIALIZATION - ISerializable, + ISerializable, IDeserializationCallback, #endif - IDictionary, + IDictionary, IDictionary, #if !NO_SPECIALIZED_COLLECTIONS - INotifyCollectionChanged, + INotifyCollectionChanged, #endif - IObservableCollection> { + IObservableCollection> { #if !NO_SERIALIZATION #region class SerializedDictionary @@ -81,6 +81,8 @@ namespace Nuclex.Support.Collections { public event EventHandler>> ItemAdded; /// Raised when an item is removed from the dictionary public event EventHandler>> ItemRemoved; + /// Raised when an item is replaced in the collection + public event EventHandler>> ItemReplaced; /// Raised when the dictionary is about to be cleared public event EventHandler Clearing; /// Raised when the dictionary has been cleared @@ -202,9 +204,13 @@ namespace Nuclex.Support.Collections { this.typedDictionary[key] = value; if(removed) { - OnRemoved(new KeyValuePair(key, oldValue)); + OnReplaced( + new KeyValuePair(key, oldValue), + new KeyValuePair(key, value) + ); + } else { + OnAdded(new KeyValuePair(key, value)); } - OnAdded(new KeyValuePair(key, value)); } } @@ -244,28 +250,51 @@ namespace Nuclex.Support.Collections { ItemAdded(this, new ItemEventArgs>(item)); #if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) + if(CollectionChanged != null) { CollectionChanged( - this, new NotifyCollectionChangedEventArgs( - NotifyCollectionChangedAction.Add, item - ) + this, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item) ); + } #endif } /// Fires the 'ItemRemoved' event /// Item that has been removed from the collection protected virtual void OnRemoved(KeyValuePair item) { - if(ItemRemoved != null) + if(ItemRemoved != null) { ItemRemoved(this, new ItemEventArgs>(item)); - + } #if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) + if(CollectionChanged != null) { + CollectionChanged( + this, + new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item) + ); + } +#endif + } + + /// Fires the 'ItemReplaced' event + /// Item that has been replaced in the collection + /// Item with which the original item was replaced + protected virtual void OnReplaced( + KeyValuePair oldItem, KeyValuePair newItem + ) { + if(ItemReplaced != null) { + ItemReplaced( + this, + new ItemReplaceEventArgs>(oldItem, newItem) + ); + } +#if !NO_SPECIALIZED_COLLECTIONS + if(CollectionChanged != null) { CollectionChanged( this, new NotifyCollectionChangedEventArgs( - NotifyCollectionChangedAction.Remove, item + NotifyCollectionChangedAction.Replace, newItem, oldItem ) ); + } #endif } @@ -281,8 +310,9 @@ namespace Nuclex.Support.Collections { Cleared(this, EventArgs.Empty); #if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) - CollectionChanged(this, CollectionResetEventArgs); + if(CollectionChanged != null) { + CollectionChanged(this, Constants.NotifyCollectionResetEventArgs); + } #endif } @@ -449,12 +479,6 @@ namespace Nuclex.Support.Collections { /// The wrapped Dictionary under its object interface private IDictionary objectDictionary; -#if !NO_SPECIALIZED_COLLECTIONS - /// Fixed event args used to notify that the collection has reset - private static readonly NotifyCollectionChangedEventArgs CollectionResetEventArgs = - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); -#endif - } } // namespace Nuclex.Support.Collections diff --git a/Source/Collections/ObservableList.cs b/Source/Collections/ObservableList.cs index eefbcfd..a04a7c1 100644 --- a/Source/Collections/ObservableList.cs +++ b/Source/Collections/ObservableList.cs @@ -30,16 +30,21 @@ 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, + public class ObservableList : + IList, + IList, + ICollection, #if !NO_SPECIALIZED_COLLECTIONS - INotifyCollectionChanged, + INotifyCollectionChanged, #endif - IObservableCollection { + 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 @@ -83,10 +88,7 @@ namespace Nuclex.Support.Collections { /// Item that will be inserted into the list public void Insert(int index, TItem item) { this.typedList.Insert(index, item); - OnAdded(item); -#if !NO_SPECIALIZED_COLLECTIONS - OnCollectionChanged(NotifyCollectionChangedAction.Add, item, index); -#endif + OnAdded(item, index); } /// Removes the item at the specified index from the list @@ -94,10 +96,7 @@ namespace Nuclex.Support.Collections { public void RemoveAt(int index) { TItem item = this.typedList[index]; this.typedList.RemoveAt(index); - OnRemoved(item); -#if !NO_SPECIALIZED_COLLECTIONS - OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index); -#endif + OnRemoved(item, index); } /// Accesses the item at the specified index in the list @@ -108,18 +107,7 @@ namespace Nuclex.Support.Collections { set { TItem oldItem = this.typedList[index]; this.typedList[index] = value; - OnRemoved(oldItem); - OnAdded(value); -#if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) { - CollectionChanged( - this, new NotifyCollectionChangedEventArgs( - NotifyCollectionChangedAction.Replace, value, oldItem, index - ) - ); - } -#endif - + OnReplaced(oldItem, value, index); } } @@ -127,7 +115,7 @@ namespace Nuclex.Support.Collections { /// Item that will be added to the list public void Add(TItem item) { this.typedList.Add(item); - OnAdded(item); + OnAdded(item, this.typedList.Count - 1); } /// Removes all items from the list @@ -135,11 +123,6 @@ namespace Nuclex.Support.Collections { OnClearing(); this.typedList.Clear(); OnCleared(); -#if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) { - CollectionChanged(this, CollectionResetEventArgs); - } -#endif } /// Checks whether the list contains the specified item @@ -181,10 +164,8 @@ namespace Nuclex.Support.Collections { TItem removedItem = this.typedList[index]; this.typedList.RemoveAt(index); - OnRemoved(removedItem); -#if !NO_SPECIALIZED_COLLECTIONS - OnCollectionChanged(NotifyCollectionChangedAction.Remove, item, index); -#endif + OnRemoved(removedItem, index); + return true; } @@ -237,10 +218,7 @@ namespace Nuclex.Support.Collections { int IList.Add(object value) { int index = this.objectList.Add(value); TItem addedItem = this.typedList[index]; - OnAdded(addedItem); -#if !NO_SPECIALIZED_COLLECTIONS - OnCollectionChanged(NotifyCollectionChangedAction.Add, addedItem, index); -#endif + OnAdded(addedItem, index); return index; } @@ -264,10 +242,7 @@ namespace Nuclex.Support.Collections { void IList.Insert(int index, object item) { this.objectList.Insert(index, item); TItem addedItem = this.typedList[index]; - OnAdded(addedItem); -#if !NO_SPECIALIZED_COLLECTIONS - OnCollectionChanged(NotifyCollectionChangedAction.Add, addedItem, index); -#endif + OnAdded(addedItem, index); } /// Whether the list is of a fixed size @@ -285,10 +260,7 @@ namespace Nuclex.Support.Collections { TItem removedItem = this.typedList[index]; this.objectList.RemoveAt(index); - OnRemoved(removedItem); -#if !NO_SPECIALIZED_COLLECTIONS - OnCollectionChanged(NotifyCollectionChangedAction.Remove, removedItem, index); -#endif + OnRemoved(removedItem, index); } /// Accesses the item at the specified index in the list @@ -300,69 +272,84 @@ namespace Nuclex.Support.Collections { TItem oldItem = this.typedList[index]; this.objectList[index] = value; TItem newItem = this.typedList[index]; - OnRemoved(oldItem); - OnAdded(newItem); -#if !NO_SPECIALIZED_COLLECTIONS - if(CollectionChanged != null) { - CollectionChanged( - this, new NotifyCollectionChangedEventArgs( - NotifyCollectionChangedAction.Replace, newItem, oldItem, index - ) - ); - } -#endif + OnReplaced(oldItem, newItem, index); } } #endregion // IList implementation -#if !NO_SPECIALIZED_COLLECTIONS - /// Fires the CollectionChanged event - /// Type of change that has occured - /// The item that has been added, removed or replaced - /// Index of the changed item - protected virtual void OnCollectionChanged( - NotifyCollectionChangedAction action, TItem item, int index - ) { - if(CollectionChanged != null) { - CollectionChanged( - this, new NotifyCollectionChangedEventArgs(action, item, index) - ); - } - } -#endif - /// Fires the 'ItemAdded' event /// Item that has been added to the collection - protected virtual void OnAdded(TItem item) { - if(ItemAdded != null) + /// 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 - protected virtual void OnRemoved(TItem item) { - if(ItemRemoved != null) + /// 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) + if(Clearing != null) { Clearing(this, EventArgs.Empty); + } } /// Fires the 'Cleared' event protected virtual void OnCleared() { - if(Cleared != null) + if(Cleared != null) { Cleared(this, EventArgs.Empty); - } - + } #if !NO_SPECIALIZED_COLLECTIONS - /// Fixed event args used to notify that the collection has reset - private static readonly NotifyCollectionChangedEventArgs CollectionResetEventArgs = - new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); + if(CollectionChanged != null) { + CollectionChanged(this, Constants.NotifyCollectionResetEventArgs); + } #endif + } /// The wrapped list under its type-safe interface private IList typedList;