Extended to IObservableCollection interface with an ItemsCleared event so subscribers observing the collection only to trigger 'Changed' events (versus subscribers observing the collection to wire/unwire themselves from the collection's items) can make use of the collection

git-svn-id: file:///srv/devel/repo-conversion/nusu@182 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
Markus Ewald 2009-10-12 19:19:58 +00:00
parent d5293d8cb9
commit 655ae7ab10
5 changed files with 54 additions and 38 deletions

View File

@ -42,6 +42,9 @@ namespace Nuclex.Support.Collections {
/// </remarks> /// </remarks>
event EventHandler Clearing; event EventHandler Clearing;
/// <summary>Raised when the collection has been cleared of its items</summary>
event EventHandler Cleared;
} }
} // namespace Nuclex.Support.Collections } // namespace Nuclex.Support.Collections

View File

@ -42,6 +42,11 @@ namespace Nuclex.Support.Collections {
/// <param name="arguments">Not used</param> /// <param name="arguments">Not used</param>
void Clearing(object sender, EventArgs arguments); void Clearing(object sender, EventArgs arguments);
/// <summary>Called when the collection has been cleared of its contents</summary>
/// <param name="sender">Collection that was cleared of its contents</param>
/// <param name="arguments">Not used</param>
void Cleared(object sender, EventArgs arguments);
/// <summary>Called when an item is added to the collection</summary> /// <summary>Called when an item is added to the collection</summary>
/// <param name="sender">Collection to which an item is being added</param> /// <param name="sender">Collection to which an item is being added</param>
/// <param name="arguments">Contains the item that is being added</param> /// <param name="arguments">Contains the item that is being added</param>
@ -64,7 +69,10 @@ namespace Nuclex.Support.Collections {
this.mockedSubscriber = this.mockery.NewMock<IObservableCollectionSubscriber>(); this.mockedSubscriber = this.mockery.NewMock<IObservableCollectionSubscriber>();
this.observedCollection = new ObservableCollection<int>(); this.observedCollection = new ObservableCollection<int>();
this.observedCollection.Clearing += new EventHandler(this.mockedSubscriber.Clearing); this.observedCollection.Clearing +=
new EventHandler(this.mockedSubscriber.Clearing);
this.observedCollection.Cleared +=
new EventHandler(this.mockedSubscriber.Cleared);
this.observedCollection.ItemAdded += this.observedCollection.ItemAdded +=
new EventHandler<ItemEventArgs<int>>( new EventHandler<ItemEventArgs<int>>(
this.mockedSubscriber.ItemAdded this.mockedSubscriber.ItemAdded
@ -78,9 +86,8 @@ namespace Nuclex.Support.Collections {
/// <summary>Tests whether the Clearing event is fired</summary> /// <summary>Tests whether the Clearing event is fired</summary>
[Test] [Test]
public void TestClearingEvent() { public void TestClearingEvent() {
Expect.Once.On(this.mockedSubscriber). Expect.Once.On(this.mockedSubscriber).Method("Clearing").WithAnyArguments();
Method("Clearing"); Expect.Once.On(this.mockedSubscriber).Method("Cleared").WithAnyArguments();
this.observedCollection.Clear(); this.observedCollection.Clear();
this.mockery.VerifyAllExpectationsHaveBeenMet(); this.mockery.VerifyAllExpectationsHaveBeenMet();
@ -89,9 +96,7 @@ namespace Nuclex.Support.Collections {
/// <summary>Tests whether the ItemAdded event is fired</summary> /// <summary>Tests whether the ItemAdded event is fired</summary>
[Test] [Test]
public void TestItemAddedEvent() { public void TestItemAddedEvent() {
Expect.Once.On(this.mockedSubscriber). Expect.Once.On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
Method("ItemAdded").
WithAnyArguments();
this.observedCollection.Add(123); this.observedCollection.Add(123);
@ -101,15 +106,11 @@ namespace Nuclex.Support.Collections {
/// <summary>Tests whether the ItemRemoved event is fired</summary> /// <summary>Tests whether the ItemRemoved event is fired</summary>
[Test] [Test]
public void TestItemRemovedEvent() { public void TestItemRemovedEvent() {
Expect.Once.On(this.mockedSubscriber). Expect.Once.On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
Method("ItemAdded").
WithAnyArguments();
this.observedCollection.Add(123); this.observedCollection.Add(123);
Expect.Once.On(this.mockedSubscriber). Expect.Once.On(this.mockedSubscriber).Method("ItemRemoved").WithAnyArguments();
Method("ItemRemoved").
WithAnyArguments();
this.observedCollection.Remove(123); this.observedCollection.Remove(123);
@ -119,28 +120,20 @@ namespace Nuclex.Support.Collections {
/// <summary>Tests whether items in the collection can be replaced</summary> /// <summary>Tests whether items in the collection can be replaced</summary>
[Test] [Test]
public void TestItemReplacement() { public void TestItemReplacement() {
Expect.Exactly(3).On(this.mockedSubscriber). Expect.Exactly(3).On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
Method("ItemAdded").
WithAnyArguments();
this.observedCollection.Add(1); this.observedCollection.Add(1);
this.observedCollection.Add(2); this.observedCollection.Add(2);
this.observedCollection.Add(3); this.observedCollection.Add(3);
Expect.Once.On(this.mockedSubscriber). Expect.Once.On(this.mockedSubscriber).Method("ItemRemoved").WithAnyArguments();
Method("ItemRemoved"). Expect.Once.On(this.mockedSubscriber).Method("ItemAdded").WithAnyArguments();
WithAnyArguments();
Expect.Once.On(this.mockedSubscriber).
Method("ItemAdded").
WithAnyArguments();
// Replace the middle item with something else // Replace the middle item with something else
this.observedCollection[1] = 4; this.observedCollection[1] = 4;
Assert.AreEqual( Assert.AreEqual(
1, 1, this.observedCollection.IndexOf(4)
this.observedCollection.IndexOf(4)
); );
this.mockery.VerifyAllExpectationsHaveBeenMet(); this.mockery.VerifyAllExpectationsHaveBeenMet();
@ -153,10 +146,7 @@ namespace Nuclex.Support.Collections {
ObservableCollection<int> testCollection = new ObservableCollection<int>(integers); ObservableCollection<int> testCollection = new ObservableCollection<int>(integers);
CollectionAssert.AreEqual( CollectionAssert.AreEqual(integers, testCollection);
integers,
testCollection
);
} }
/// <summary>Mock object factory</summary> /// <summary>Mock object factory</summary>

View File

@ -39,6 +39,8 @@ namespace Nuclex.Support.Collections {
/// to process the clearing of the entire collection as a special operation. /// to process the clearing of the entire collection as a special operation.
/// </remarks> /// </remarks>
public event EventHandler Clearing; public event EventHandler Clearing;
/// <summary>Raised when the collection has been cleared</summary>
public event EventHandler Cleared;
/// <summary> /// <summary>
/// Initializes a new instance of the ObservableCollection class that is empty. /// Initializes a new instance of the ObservableCollection class that is empty.
@ -58,8 +60,8 @@ namespace Nuclex.Support.Collections {
/// <summary>Removes all elements from the Collection</summary> /// <summary>Removes all elements from the Collection</summary>
protected override void ClearItems() { protected override void ClearItems() {
OnClearing(); OnClearing();
base.ClearItems(); base.ClearItems();
OnCleared();
} }
/// <summary> /// <summary>
@ -71,7 +73,6 @@ namespace Nuclex.Support.Collections {
/// <param name="item">The zero-based index at which item should be inserted</param> /// <param name="item">The zero-based index at which item should be inserted</param>
protected override void InsertItem(int index, ItemType item) { protected override void InsertItem(int index, ItemType item) {
base.InsertItem(index, item); base.InsertItem(index, item);
OnAdded(item); OnAdded(item);
} }
@ -81,9 +82,7 @@ namespace Nuclex.Support.Collections {
/// <param name="index">The zero-based index of the element to remove</param> /// <param name="index">The zero-based index of the element to remove</param>
protected override void RemoveItem(int index) { protected override void RemoveItem(int index) {
ItemType item = base[index]; ItemType item = base[index];
base.RemoveItem(index); base.RemoveItem(index);
OnRemoved(item); OnRemoved(item);
} }
@ -95,9 +94,7 @@ namespace Nuclex.Support.Collections {
/// <param name="item">The zero-based index of the element to replace</param> /// <param name="item">The zero-based index of the element to replace</param>
protected override void SetItem(int index, ItemType item) { protected override void SetItem(int index, ItemType item) {
ItemType oldItem = base[index]; ItemType oldItem = base[index];
base.SetItem(index, item); base.SetItem(index, item);
OnRemoved(oldItem); OnRemoved(oldItem);
OnAdded(item); OnAdded(item);
} }
@ -122,6 +119,12 @@ namespace Nuclex.Support.Collections {
Clearing(this, EventArgs.Empty); Clearing(this, EventArgs.Empty);
} }
/// <summary>Fires the 'Cleared' event</summary>
protected virtual void OnCleared() {
if(Cleared != null)
Cleared(this, EventArgs.Empty);
}
} }
} // namespace Nuclex.Support.Collections } // namespace Nuclex.Support.Collections

View File

@ -45,6 +45,11 @@ namespace Nuclex.Support.Collections {
/// <param name="arguments">Not used</param> /// <param name="arguments">Not used</param>
void Clearing(object sender, EventArgs arguments); void Clearing(object sender, EventArgs arguments);
/// <summary>Called when the dictionary has been clear of its contents</summary>
/// <param name="sender">Dictionary that was cleared of its contents</param>
/// <param name="arguments">Not used</param>
void Cleared(object sender, EventArgs arguments);
/// <summary>Called when an item is added to the dictionary</summary> /// <summary>Called when an item is added to the dictionary</summary>
/// <param name="sender">Dictionary to which an item is being added</param> /// <param name="sender">Dictionary to which an item is being added</param>
/// <param name="arguments">Contains the item that is being added</param> /// <param name="arguments">Contains the item that is being added</param>
@ -72,7 +77,10 @@ namespace Nuclex.Support.Collections {
this.observedDictionary.Add(3, "three"); this.observedDictionary.Add(3, "three");
this.observedDictionary.Add(42, "forty-two"); this.observedDictionary.Add(42, "forty-two");
this.observedDictionary.Clearing += new EventHandler(this.mockedSubscriber.Clearing); this.observedDictionary.Clearing +=
new EventHandler(this.mockedSubscriber.Clearing);
this.observedDictionary.Cleared +=
new EventHandler(this.mockedSubscriber.Cleared);
this.observedDictionary.ItemAdded += this.observedDictionary.ItemAdded +=
new EventHandler<ItemEventArgs<KeyValuePair<int, string>>>( new EventHandler<ItemEventArgs<KeyValuePair<int, string>>>(
this.mockedSubscriber.ItemAdded this.mockedSubscriber.ItemAdded
@ -293,6 +301,7 @@ namespace Nuclex.Support.Collections {
[Test] [Test]
public void TestClearViaIDictionary() { public void TestClearViaIDictionary() {
Expect.Once.On(this.mockedSubscriber).Method("Clearing").WithAnyArguments(); Expect.Once.On(this.mockedSubscriber).Method("Clearing").WithAnyArguments();
Expect.Once.On(this.mockedSubscriber).Method("Cleared").WithAnyArguments();
(this.observedDictionary as IDictionary).Clear(); (this.observedDictionary as IDictionary).Clear();
this.mockery.VerifyAllExpectationsHaveBeenMet(); this.mockery.VerifyAllExpectationsHaveBeenMet();
@ -424,6 +433,7 @@ namespace Nuclex.Support.Collections {
[Test] [Test]
public void TestClearViaGenericICollection() { public void TestClearViaGenericICollection() {
Expect.Once.On(this.mockedSubscriber).Method("Clearing").WithAnyArguments(); Expect.Once.On(this.mockedSubscriber).Method("Clearing").WithAnyArguments();
Expect.Once.On(this.mockedSubscriber).Method("Cleared").WithAnyArguments();
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Clear(); (this.observedDictionary as ICollection<KeyValuePair<int, string>>).Clear();
this.mockery.VerifyAllExpectationsHaveBeenMet(); this.mockery.VerifyAllExpectationsHaveBeenMet();

View File

@ -32,7 +32,7 @@ namespace Nuclex.Support.Collections {
[Serializable] [Serializable]
public class ObservableDictionary<KeyType, ValueType> : public class ObservableDictionary<KeyType, ValueType> :
#if !NO_SERIALIZATION #if !NO_SERIALIZATION
ISerializable, ISerializable,
IDeserializationCallback, IDeserializationCallback,
#endif #endif
IDictionary<KeyType, ValueType>, IDictionary<KeyType, ValueType>,
@ -76,6 +76,8 @@ namespace Nuclex.Support.Collections {
public event EventHandler<ItemEventArgs<KeyValuePair<KeyType, ValueType>>> ItemRemoved; public event EventHandler<ItemEventArgs<KeyValuePair<KeyType, ValueType>>> ItemRemoved;
/// <summary>Raised when the dictionary is about to be cleared</summary> /// <summary>Raised when the dictionary is about to be cleared</summary>
public event EventHandler Clearing; public event EventHandler Clearing;
/// <summary>Raised when the dictionary has been cleared</summary>
public event EventHandler Cleared;
/// <summary>Initializes a new observable dictionary</summary> /// <summary>Initializes a new observable dictionary</summary>
public ObservableDictionary() : this(new Dictionary<KeyType, ValueType>()) { } public ObservableDictionary() : this(new Dictionary<KeyType, ValueType>()) { }
@ -220,6 +222,7 @@ namespace Nuclex.Support.Collections {
public void Clear() { public void Clear() {
OnClearing(); OnClearing();
this.typedDictionary.Clear(); this.typedDictionary.Clear();
OnCleared();
} }
/// <summary>Fires the 'ItemAdded' event</summary> /// <summary>Fires the 'ItemAdded' event</summary>
@ -242,6 +245,12 @@ namespace Nuclex.Support.Collections {
Clearing(this, EventArgs.Empty); Clearing(this, EventArgs.Empty);
} }
/// <summary>Fires the 'Cleared' event</summary>
protected virtual void OnCleared() {
if(Cleared != null)
Cleared(this, EventArgs.Empty);
}
#region IEnumerable implementation #region IEnumerable implementation
/// <summary>Returns a new object enumerator for the Dictionary</summary> /// <summary>Returns a new object enumerator for the Dictionary</summary>
@ -337,6 +346,7 @@ namespace Nuclex.Support.Collections {
void ICollection<KeyValuePair<KeyType, ValueType>>.Clear() { void ICollection<KeyValuePair<KeyType, ValueType>>.Clear() {
OnClearing(); OnClearing();
this.typedDictionary.Clear(); this.typedDictionary.Clear();
OnCleared();
} }
/// <summary>Removes all items from the Dictionary</summary> /// <summary>Removes all items from the Dictionary</summary>