Browse Source

Added ResetBehavior to PerspexList.

As sometimes you want to know something is a reset rather than a remove.
pull/278/head
Steven Kirk 11 years ago
parent
commit
d87bcde7be
  1. 7
      src/Perspex.Animation/PropertyTransitions.cs
  2. 96
      src/Perspex.Base/Collections/PerspexList.cs
  3. 2
      src/Perspex.Controls/ColumnDefinitions.cs
  4. 4
      src/Perspex.Controls/Control.cs
  5. 2
      src/Perspex.Controls/Controls.cs
  6. 2
      src/Perspex.Controls/RowDefinitions.cs
  7. 7
      src/Perspex.Controls/Templates/DataTemplates.cs
  8. 1
      src/Perspex.SceneGraph/Visual.cs
  9. 20
      tests/Perspex.Base.UnitTests/Collections/PerspexListTests.cs

7
src/Perspex.Animation/PropertyTransitions.cs

@ -10,5 +10,12 @@ namespace Perspex.Animation
/// </summary> /// </summary>
public class PropertyTransitions : PerspexList<PropertyTransition> public class PropertyTransitions : PerspexList<PropertyTransition>
{ {
/// <summary>
/// Initializes a new instance of the <see cref="PropertyTransitions"/> class.
/// </summary>
public PropertyTransitions()
{
ResetBehavior = ResetBehavior.Remove;
}
} }
} }

96
src/Perspex.Base/Collections/PerspexList.cs

@ -10,15 +10,47 @@ using System.Linq;
namespace Perspex.Collections namespace Perspex.Collections
{ {
/// <summary>
/// Describes the action notified on a clear of a <see cref="PerspexList{T}"/>.
/// </summary>
public enum ResetBehavior
{
/// <summary>
/// Clearing the list notifies a with a
/// <see cref="NotifyCollectionChangedAction.Reset"/>.
/// </summary>
Reset,
/// <summary>
/// Clearing the list notifies a with a
/// <see cref="NotifyCollectionChangedAction.Remove"/>.
/// </summary>
Remove,
}
/// <summary> /// <summary>
/// A notifying list. /// A notifying list.
/// </summary> /// </summary>
/// <typeparam name="T">The type of the list items.</typeparam> /// <typeparam name="T">The type of the list items.</typeparam>
/// <remarks> /// <remarks>
/// <para>
/// PerspexList is similar to <see cref="System.Collections.ObjectModel.ObservableCollection{T}"/> /// PerspexList is similar to <see cref="System.Collections.ObjectModel.ObservableCollection{T}"/>
/// except that when the <see cref="Clear"/> method is called, it notifies with a /// with a few added features:
/// <see cref="NotifyCollectionChangedAction.Remove"/> action, passing the items that were /// </para>
/// removed. ///
/// <list type="bullet">
/// <item>
/// It can be configured to notify the <see cref="CollectionChanged"/> event with a
/// <see cref="NotifyCollectionChangedAction.Remove"/> action instead of a
/// <see cref="NotifyCollectionChangedAction.Reset"/> when the list is cleared by
/// setting <see cref="ResetBehavior"/> to <see cref="ResetBehavior.Remove"/>.
/// removed
/// </item>
/// <item>
/// A <see cref="Validate"/> function can be used to validate each item before insertion.
/// removed
/// </item>
/// </list>
/// </remarks> /// </remarks>
public class PerspexList<T> : IPerspexList<T>, IList, INotifyCollectionChanged, INotifyPropertyChanged public class PerspexList<T> : IPerspexList<T>, IList, INotifyCollectionChanged, INotifyPropertyChanged
{ {
@ -65,6 +97,17 @@ namespace Perspex.Collections
/// </summary> /// </summary>
public int Count => _inner.Count; public int Count => _inner.Count;
/// <summary>
/// Gets or sets the reset behavior of the list.
/// </summary>
public ResetBehavior ResetBehavior { get; set; }
/// <summary>
/// Gets or sets a validation routine that can be used to validate items before they are
/// added.
/// </summary>
public Action<T> Validate { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
bool IList.IsFixedSize => false; bool IList.IsFixedSize => false;
@ -97,6 +140,8 @@ namespace Perspex.Collections
set set
{ {
Validate?.Invoke(value);
T old = _inner[index]; T old = _inner[index];
_inner[index] = value; _inner[index] = value;
@ -128,6 +173,7 @@ namespace Perspex.Collections
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
public void Add(T item) public void Add(T item)
{ {
Validate?.Invoke(item);
int index = _inner.Count; int index = _inner.Count;
_inner.Add(item); _inner.Add(item);
NotifyAdd(new[] { item }, index); NotifyAdd(new[] { item }, index);
@ -141,6 +187,14 @@ namespace Perspex.Collections
{ {
Contract.Requires<ArgumentNullException>(items != null); Contract.Requires<ArgumentNullException>(items != null);
if (Validate != null)
{
foreach (var item in items)
{
Validate(item);
}
}
int index = _inner.Count; int index = _inner.Count;
_inner.AddRange(items); _inner.AddRange(items);
NotifyAdd((items as IList) ?? items.ToList(), index); NotifyAdd((items as IList) ?? items.ToList(), index);
@ -153,7 +207,7 @@ namespace Perspex.Collections
{ {
var old = _inner; var old = _inner;
_inner = new List<T>(); _inner = new List<T>();
NotifyRemove(old, 0); NotifyReset(old);
} }
/// <summary> /// <summary>
@ -204,6 +258,7 @@ namespace Perspex.Collections
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
public void Insert(int index, T item) public void Insert(int index, T item)
{ {
Validate?.Invoke(item);
_inner.Insert(index, item); _inner.Insert(index, item);
NotifyAdd(new[] { item }, index); NotifyAdd(new[] { item }, index);
} }
@ -217,6 +272,14 @@ namespace Perspex.Collections
{ {
Contract.Requires<ArgumentNullException>(items != null); Contract.Requires<ArgumentNullException>(items != null);
if (Validate != null)
{
foreach (var item in items)
{
Validate(item);
}
}
_inner.InsertRange(index, items); _inner.InsertRange(index, items);
NotifyAdd((items as IList) ?? items.ToList(), index); NotifyAdd((items as IList) ?? items.ToList(), index);
} }
@ -367,5 +430,30 @@ namespace Perspex.Collections
NotifyCountChanged(); NotifyCountChanged();
} }
/// <summary>
/// Raises the <see cref="CollectionChanged"/> event with a reset action.
/// </summary>
/// <param name="t">The items that were removed.</param>
private void NotifyReset(IList t)
{
if (CollectionChanged != null)
{
NotifyCollectionChangedEventArgs e;
if (ResetBehavior == ResetBehavior.Reset)
{
e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
}
else
{
e = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, t, 0);
}
CollectionChanged(this, e);
}
NotifyCountChanged();
}
} }
} }

2
src/Perspex.Controls/ColumnDefinitions.cs

@ -17,6 +17,7 @@ namespace Perspex.Controls
/// </summary> /// </summary>
public ColumnDefinitions() public ColumnDefinitions()
{ {
ResetBehavior = ResetBehavior.Remove;
} }
/// <summary> /// <summary>
@ -24,6 +25,7 @@ namespace Perspex.Controls
/// </summary> /// </summary>
/// <param name="s">A string representation of the column definitions.</param> /// <param name="s">A string representation of the column definitions.</param>
public ColumnDefinitions(string s) public ColumnDefinitions(string s)
: this()
{ {
AddRange(GridLength.ParseLengths(s, CultureInfo.InvariantCulture).Select(x => new ColumnDefinition(x))); AddRange(GridLength.ParseLengths(s, CultureInfo.InvariantCulture).Select(x => new ColumnDefinition(x)));
} }

4
src/Perspex.Controls/Control.cs

@ -271,7 +271,9 @@ namespace Perspex.Controls
{ {
if (_logicalChildren == null) if (_logicalChildren == null)
{ {
_logicalChildren = new PerspexList<ILogical>(); var list = new PerspexList<ILogical>();
list.ResetBehavior = ResetBehavior.Remove;
_logicalChildren = list;
} }
return _logicalChildren; return _logicalChildren;

2
src/Perspex.Controls/Controls.cs

@ -16,6 +16,7 @@ namespace Perspex.Controls
/// </summary> /// </summary>
public Controls() public Controls()
{ {
ResetBehavior = ResetBehavior.Remove;
} }
/// <summary> /// <summary>
@ -25,6 +26,7 @@ namespace Perspex.Controls
public Controls(IEnumerable<IControl> items) public Controls(IEnumerable<IControl> items)
: base(items) : base(items)
{ {
ResetBehavior = ResetBehavior.Remove;
} }
} }
} }

2
src/Perspex.Controls/RowDefinitions.cs

@ -17,6 +17,7 @@ namespace Perspex.Controls
/// </summary> /// </summary>
public RowDefinitions() public RowDefinitions()
{ {
ResetBehavior = ResetBehavior.Remove;
} }
/// <summary> /// <summary>
@ -24,6 +25,7 @@ namespace Perspex.Controls
/// </summary> /// </summary>
/// <param name="s">A string representation of the row definitions.</param> /// <param name="s">A string representation of the row definitions.</param>
public RowDefinitions(string s) public RowDefinitions(string s)
: this()
{ {
AddRange(GridLength.ParseLengths(s, CultureInfo.InvariantCulture).Select(x => new RowDefinition(x))); AddRange(GridLength.ParseLengths(s, CultureInfo.InvariantCulture).Select(x => new RowDefinition(x)));
} }

7
src/Perspex.Controls/Templates/DataTemplates.cs

@ -10,5 +10,12 @@ namespace Perspex.Controls.Templates
/// </summary> /// </summary>
public class DataTemplates : PerspexList<IDataTemplate> public class DataTemplates : PerspexList<IDataTemplate>
{ {
/// <summary>
/// Initializes a new instance of the <see cref="DataTemplates"/> class.
/// </summary>
public DataTemplates()
{
ResetBehavior = ResetBehavior.Remove;
}
} }
} }

1
src/Perspex.SceneGraph/Visual.cs

@ -124,6 +124,7 @@ namespace Perspex
}); });
_visualChildren = new PerspexList<IVisual>(); _visualChildren = new PerspexList<IVisual>();
_visualChildren.ResetBehavior = ResetBehavior.Remove;
_visualChildren.CollectionChanged += VisualChildrenChanged; _visualChildren.CollectionChanged += VisualChildrenChanged;
} }

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

@ -158,12 +158,32 @@ namespace Perspex.Base.UnitTests.Collections
Assert.True(raised); Assert.True(raised);
} }
[Fact]
public void Clearing_Items_Should_Raise_CollectionChanged_Reset()
{
var target = new PerspexList<int>(new[] { 1, 2, 3 });
var raised = false;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);
Assert.Equal(NotifyCollectionChangedAction.Reset, e.Action);
raised = true;
};
target.Clear();
Assert.True(raised);
}
[Fact] [Fact]
public void Clearing_Items_Should_Raise_CollectionChanged_Remove() public void Clearing_Items_Should_Raise_CollectionChanged_Remove()
{ {
var target = new PerspexList<int>(new[] { 1, 2, 3 }); var target = new PerspexList<int>(new[] { 1, 2, 3 });
var raised = false; var raised = false;
target.ResetBehavior = ResetBehavior.Remove;
target.CollectionChanged += (s, e) => target.CollectionChanged += (s, e) =>
{ {
Assert.Equal(target, s); Assert.Equal(target, s);

Loading…
Cancel
Save