Browse Source

Added PerspexDictionary.

pull/90/head
Steven Kirk 11 years ago
parent
commit
b036be75ee
  1. 237
      src/Perspex.Base/Collections/PerspexDictionary.cs
  2. 1
      src/Perspex.Base/Perspex.Base.csproj
  3. 36
      tests/Perspex.Base.UnitTests/Collections/CollectionChangedTracker.cs
  4. 154
      tests/Perspex.Base.UnitTests/Collections/PerspexDictionaryTests.cs
  5. 2
      tests/Perspex.Base.UnitTests/Collections/PerspexListTests.cs
  6. 33
      tests/Perspex.Base.UnitTests/Collections/PropertyChangedTracker.cs
  7. 2
      tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj

237
src/Perspex.Base/Collections/PerspexDictionary.cs

@ -0,0 +1,237 @@
// -----------------------------------------------------------------------
// <copyright file="PerspexDictionary.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Collections
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
/// <summary>
/// A notifying dictionary.
/// </summary>
/// <typeparam name="TKey">The type of the dictionary key.</typeparam>
/// <typeparam name="TValue">The type of the dictionary value.</typeparam>
public class PerspexDictionary<TKey, TValue> : IDictionary<TKey, TValue>,
INotifyCollectionChanged,
INotifyPropertyChanged
{
private Dictionary<TKey, TValue> inner;
/// <summary>
/// Initializes a new instance of the <see cref="PerspexDictionary{TKey, TValue}"/> class.
/// </summary>
public PerspexDictionary()
{
this.inner = new Dictionary<TKey, TValue>();
}
/// <summary>
/// Occurs when the collection changes.
/// </summary>
public event NotifyCollectionChangedEventHandler CollectionChanged;
/// <summary>
/// Raised when a property on the collection changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <inheritdoc/>
public int Count
{
get { return this.inner.Count; }
}
/// <inheritdoc/>
public bool IsReadOnly
{
get { return false; }
}
/// <inheritdoc/>
public ICollection<TKey> Keys
{
get { return this.inner.Keys; }
}
/// <inheritdoc/>
public ICollection<TValue> Values
{
get { return this.inner.Values; }
}
/// <summary>
/// Gets or sets the named resource.
/// </summary>
/// <param name="key">The resource key.</param>
/// <returns>The resource, or null if not found.</returns>
public TValue this[TKey key]
{
get
{
return this.inner[key];
}
set
{
TValue old;
bool replace = this.inner.TryGetValue(key, out old);
this.inner[key] = value;
if (replace)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
if (this.CollectionChanged != null)
{
var e = new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Replace,
new KeyValuePair<TKey, TValue>(key, value),
new KeyValuePair<TKey, TValue>(key, old));
this.CollectionChanged(this, e);
}
}
else
{
this.NotifyAdd(key, value);
}
}
}
/// <inheritdoc/>
public void Add(TKey key, TValue value)
{
this.inner.Add(key, value);
this.NotifyAdd(key, value);
}
/// <inheritdoc/>
public void Clear()
{
var old = this.inner;
this.inner = new Dictionary<TKey, TValue>();
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
this.PropertyChanged(this, new PropertyChangedEventArgs($"Item[]"));
}
if (this.CollectionChanged != null)
{
var e = new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove,
old.ToList(),
-1);
this.CollectionChanged(this, e);
}
}
/// <inheritdoc/>
public bool ContainsKey(TKey key)
{
return this.inner.ContainsKey(key);
}
/// <inheritdoc/>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
((IDictionary<TKey, TValue>)this.inner).CopyTo(array, arrayIndex);
}
/// <inheritdoc/>
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
return this.inner.GetEnumerator();
}
/// <inheritdoc/>
public bool Remove(TKey key)
{
TValue value;
if (this.inner.TryGetValue(key, out value))
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
this.PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
if (this.CollectionChanged != null)
{
var e = new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Remove,
new[] { new KeyValuePair<TKey, TValue>(key, value) },
-1);
this.CollectionChanged(this, e);
}
return true;
}
else
{
return false;
}
}
/// <inheritdoc/>
public bool TryGetValue(TKey key, out TValue value)
{
return this.inner.TryGetValue(key, out value);
}
/// <inheritdoc/>
IEnumerator IEnumerable.GetEnumerator()
{
return this.inner.GetEnumerator();
}
/// <inheritdoc/>
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
this.Add(item.Key, item.Value);
}
/// <inheritdoc/>
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return this.inner.Contains(item);
}
/// <inheritdoc/>
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return this.Remove(item.Key);
}
private void NotifyAdd(TKey key, TValue value)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs("Count"));
this.PropertyChanged(this, new PropertyChangedEventArgs($"Item[{key}]"));
}
if (this.CollectionChanged != null)
{
var val = new KeyValuePair<TKey, TValue>(key, value);
var e = new NotifyCollectionChangedEventArgs(
NotifyCollectionChangedAction.Add,
new[] { new KeyValuePair<TKey, TValue>(key, value) },
-1);
this.CollectionChanged(this, e);
}
}
}
}

1
src/Perspex.Base/Perspex.Base.csproj

@ -42,6 +42,7 @@
<Link>Properties\SharedAssemblyInfo.cs</Link>
</Compile>
<Compile Include="BindingDescriptor.cs" />
<Compile Include="Collections\PerspexDictionary.cs" />
<Compile Include="Diagnostics\PerspexObjectExtensions.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="IObservablePropertyBag.cs" />

36
tests/Perspex.Base.UnitTests/Collections/CollectionChangedTracker.cs

@ -0,0 +1,36 @@
// -----------------------------------------------------------------------
// <copyright file="CollectionChangedTracker.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Base.UnitTests.Collections
{
using System;
using System.Collections.Specialized;
internal class CollectionChangedTracker
{
public CollectionChangedTracker(INotifyCollectionChanged collection)
{
collection.CollectionChanged += this.CollectionChanged;
}
public NotifyCollectionChangedEventArgs Args { get; private set; }
public void Reset()
{
this.Args = null;
}
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (this.Args != null)
{
throw new Exception("CollectionChanged called more than once.");
}
this.Args = e;
}
}
}

154
tests/Perspex.Base.UnitTests/Collections/PerspexDictionaryTests.cs

@ -0,0 +1,154 @@
// -----------------------------------------------------------------------
// <copyright file="PerspexDictionaryTests.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Base.UnitTests.Collections
{
using System.Collections.Generic;
using System.Collections.Specialized;
using Perspex.Collections;
using Xunit;
public class PerspexDictionaryTests
{
[Fact]
public void Adding_Item_Should_Raise_CollectionChanged()
{
var target = new PerspexDictionary<string, string>();
var tracker = new CollectionChangedTracker(target);
target.Add("foo", "bar");
Assert.NotNull(tracker.Args);
Assert.Equal(NotifyCollectionChangedAction.Add, tracker.Args.Action);
Assert.Equal(-1, tracker.Args.NewStartingIndex);
Assert.Equal(1, tracker.Args.NewItems.Count);
Assert.Equal(new KeyValuePair<string, string>("foo", "bar"), tracker.Args.NewItems[0]);
}
[Fact]
public void Adding_Item_Should_Raise_PropertyChanged()
{
var target = new PerspexDictionary<string, string>();
var tracker = new PropertyChangedTracker(target);
target.Add("foo", "bar");
Assert.Equal(new[] { "Count", "Item[foo]" }, tracker.Names);
}
[Fact]
public void Assigning_Item_Should_Raise_CollectionChanged_Add()
{
var target = new PerspexDictionary<string, string>();
var tracker = new CollectionChangedTracker(target);
target["foo"] = "bar";
Assert.NotNull(tracker.Args);
Assert.Equal(NotifyCollectionChangedAction.Add, tracker.Args.Action);
Assert.Equal(-1, tracker.Args.NewStartingIndex);
Assert.Equal(1, tracker.Args.NewItems.Count);
Assert.Equal(new KeyValuePair<string, string>("foo", "bar"), tracker.Args.NewItems[0]);
}
[Fact]
public void Assigning_Item_Should_Raise_CollectionChanged_Replace()
{
var target = new PerspexDictionary<string, string>();
target["foo"] = "baz";
var tracker = new CollectionChangedTracker(target);
target["foo"] = "bar";
Assert.NotNull(tracker.Args);
Assert.Equal(NotifyCollectionChangedAction.Replace, tracker.Args.Action);
Assert.Equal(-1, tracker.Args.NewStartingIndex);
Assert.Equal(1, tracker.Args.NewItems.Count);
Assert.Equal(new KeyValuePair<string, string>("foo", "bar"), tracker.Args.NewItems[0]);
}
[Fact]
public void Assigning_Item_Should_Raise_PropertyChanged_Add()
{
var target = new PerspexDictionary<string, string>();
var tracker = new PropertyChangedTracker(target);
target["foo"] = "bar";
Assert.Equal(new[] { "Count", "Item[foo]" }, tracker.Names);
}
[Fact]
public void Assigning_Item_Should_Raise_PropertyChanged_Replace()
{
var target = new PerspexDictionary<string, string>();
target["foo"] = "baz";
var tracker = new PropertyChangedTracker(target);
target["foo"] = "bar";
Assert.Equal(new[] { "Item[foo]" }, tracker.Names);
}
[Fact]
public void Removing_Item_Should_Raise_CollectionChanged()
{
var target = new PerspexDictionary<string, string>();
target["foo"] = "bar";
var tracker = new CollectionChangedTracker(target);
target.Remove("foo");
Assert.NotNull(tracker.Args);
Assert.Equal(NotifyCollectionChangedAction.Remove, tracker.Args.Action);
Assert.Equal(-1, tracker.Args.OldStartingIndex);
Assert.Equal(1, tracker.Args.OldItems.Count);
Assert.Equal(new KeyValuePair<string, string>("foo", "bar"), tracker.Args.OldItems[0]);
}
[Fact]
public void Removing_Item_Should_Raise_PropertyChanged()
{
var target = new PerspexDictionary<string, string>();
target["foo"] = "bar";
var tracker = new PropertyChangedTracker(target);
target.Remove("foo");
Assert.Equal(new[] { "Count", "Item[foo]" }, tracker.Names);
}
[Fact]
public void Clearing_Collection_Should_Raise_CollectionChanged()
{
var target = new PerspexDictionary<string, string>();
target["foo"] = "bar";
target["baz"] = "qux";
var tracker = new CollectionChangedTracker(target);
target.Clear();
Assert.NotNull(tracker.Args);
Assert.Equal(NotifyCollectionChangedAction.Remove, tracker.Args.Action);
Assert.Equal(-1, tracker.Args.OldStartingIndex);
Assert.Equal(2, tracker.Args.OldItems.Count);
Assert.Equal(new KeyValuePair<string, string>("foo", "bar"), tracker.Args.OldItems[0]);
}
[Fact]
public void Clearing_Collection_Should_Raise_PropertyChanged()
{
var target = new PerspexDictionary<string, string>();
target["foo"] = "bar";
target["baz"] = "qux";
var tracker = new PropertyChangedTracker(target);
target.Clear();
Assert.Equal(new[] { "Count", "Item[]" }, tracker.Names);
}
}
}

2
tests/Perspex.Base.UnitTests/Collections/PerspexListTests.cs

@ -4,7 +4,7 @@
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Base.UnitTests
namespace Perspex.Base.UnitTests.Collections
{
using System;
using System.Collections.Generic;

33
tests/Perspex.Base.UnitTests/Collections/PropertyChangedTracker.cs

@ -0,0 +1,33 @@
// -----------------------------------------------------------------------
// <copyright file="PropertyChangedTracker.cs" company="Steven Kirk">
// Copyright 2015 MIT Licence. See licence.md for more information.
// </copyright>
// -----------------------------------------------------------------------
namespace Perspex.Base.UnitTests.Collections
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
internal class PropertyChangedTracker
{
public PropertyChangedTracker(INotifyPropertyChanged obj)
{
this.Names = new List<string>();
obj.PropertyChanged += this.PropertyChanged;
}
public List<string> Names { get; private set; }
public void Reset()
{
this.Names.Clear();
}
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.Names.Add(e.PropertyName);
}
}
}

2
tests/Perspex.Base.UnitTests/Perspex.Base.UnitTests.csproj

@ -76,6 +76,8 @@
<Otherwise />
</Choose>
<ItemGroup>
<Compile Include="Collections\CollectionChangedTracker.cs" />
<Compile Include="Collections\PerspexDictionaryTests.cs" />
<Compile Include="Collections\PerspexListTests.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="PerspexObjectTests_GetObservable.cs" />

Loading…
Cancel
Save