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>
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
{
/// <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>
/// A notifying list.
/// </summary>
/// <typeparam name="T">The type of the list items.</typeparam>
/// <remarks>
/// <para>
/// 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
/// <see cref="NotifyCollectionChangedAction.Remove"/> action, passing the items that were
/// removed.
/// with a few added features:
/// </para>
///
/// <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>
public class PerspexList<T> : IPerspexList<T>, IList, INotifyCollectionChanged, INotifyPropertyChanged
{
@ -65,6 +97,17 @@ namespace Perspex.Collections
/// </summary>
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/>
bool IList.IsFixedSize => false;
@ -97,6 +140,8 @@ namespace Perspex.Collections
set
{
Validate?.Invoke(value);
T old = _inner[index];
_inner[index] = value;
@ -128,6 +173,7 @@ namespace Perspex.Collections
/// <param name="item">The item.</param>
public void Add(T item)
{
Validate?.Invoke(item);
int index = _inner.Count;
_inner.Add(item);
NotifyAdd(new[] { item }, index);
@ -141,6 +187,14 @@ namespace Perspex.Collections
{
Contract.Requires<ArgumentNullException>(items != null);
if (Validate != null)
{
foreach (var item in items)
{
Validate(item);
}
}
int index = _inner.Count;
_inner.AddRange(items);
NotifyAdd((items as IList) ?? items.ToList(), index);
@ -153,7 +207,7 @@ namespace Perspex.Collections
{
var old = _inner;
_inner = new List<T>();
NotifyRemove(old, 0);
NotifyReset(old);
}
/// <summary>
@ -204,6 +258,7 @@ namespace Perspex.Collections
/// <param name="item">The item.</param>
public void Insert(int index, T item)
{
Validate?.Invoke(item);
_inner.Insert(index, item);
NotifyAdd(new[] { item }, index);
}
@ -217,6 +272,14 @@ namespace Perspex.Collections
{
Contract.Requires<ArgumentNullException>(items != null);
if (Validate != null)
{
foreach (var item in items)
{
Validate(item);
}
}
_inner.InsertRange(index, items);
NotifyAdd((items as IList) ?? items.ToList(), index);
}
@ -367,5 +430,30 @@ namespace Perspex.Collections
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>
public ColumnDefinitions()
{
ResetBehavior = ResetBehavior.Remove;
}
/// <summary>
@ -24,6 +25,7 @@ namespace Perspex.Controls
/// </summary>
/// <param name="s">A string representation of the column definitions.</param>
public ColumnDefinitions(string s)
: this()
{
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)
{
_logicalChildren = new PerspexList<ILogical>();
var list = new PerspexList<ILogical>();
list.ResetBehavior = ResetBehavior.Remove;
_logicalChildren = list;
}
return _logicalChildren;

2
src/Perspex.Controls/Controls.cs

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

2
src/Perspex.Controls/RowDefinitions.cs

@ -17,6 +17,7 @@ namespace Perspex.Controls
/// </summary>
public RowDefinitions()
{
ResetBehavior = ResetBehavior.Remove;
}
/// <summary>
@ -24,6 +25,7 @@ namespace Perspex.Controls
/// </summary>
/// <param name="s">A string representation of the row definitions.</param>
public RowDefinitions(string s)
: this()
{
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>
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.ResetBehavior = ResetBehavior.Remove;
_visualChildren.CollectionChanged += VisualChildrenChanged;
}

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

@ -158,12 +158,32 @@ namespace Perspex.Base.UnitTests.Collections
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]
public void Clearing_Items_Should_Raise_CollectionChanged_Remove()
{
var target = new PerspexList<int>(new[] { 1, 2, 3 });
var raised = false;
target.ResetBehavior = ResetBehavior.Remove;
target.CollectionChanged += (s, e) =>
{
Assert.Equal(target, s);

Loading…
Cancel
Save