From d87bcde7be76ca1ab8d91258dfc7744ef1dbeced Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 8 Oct 2015 19:30:56 +0200 Subject: [PATCH] Added ResetBehavior to PerspexList. As sometimes you want to know something is a reset rather than a remove. --- src/Perspex.Animation/PropertyTransitions.cs | 7 ++ src/Perspex.Base/Collections/PerspexList.cs | 96 ++++++++++++++++++- src/Perspex.Controls/ColumnDefinitions.cs | 2 + src/Perspex.Controls/Control.cs | 4 +- src/Perspex.Controls/Controls.cs | 2 + src/Perspex.Controls/RowDefinitions.cs | 2 + .../Templates/DataTemplates.cs | 7 ++ src/Perspex.SceneGraph/Visual.cs | 1 + .../Collections/PerspexListTests.cs | 20 ++++ 9 files changed, 136 insertions(+), 5 deletions(-) diff --git a/src/Perspex.Animation/PropertyTransitions.cs b/src/Perspex.Animation/PropertyTransitions.cs index ab2fbc68c3..42d2b76904 100644 --- a/src/Perspex.Animation/PropertyTransitions.cs +++ b/src/Perspex.Animation/PropertyTransitions.cs @@ -10,5 +10,12 @@ namespace Perspex.Animation /// public class PropertyTransitions : PerspexList { + /// + /// Initializes a new instance of the class. + /// + public PropertyTransitions() + { + ResetBehavior = ResetBehavior.Remove; + } } } diff --git a/src/Perspex.Base/Collections/PerspexList.cs b/src/Perspex.Base/Collections/PerspexList.cs index 78c2de0b8d..2e8746b071 100644 --- a/src/Perspex.Base/Collections/PerspexList.cs +++ b/src/Perspex.Base/Collections/PerspexList.cs @@ -10,15 +10,47 @@ using System.Linq; namespace Perspex.Collections { + /// + /// Describes the action notified on a clear of a . + /// + public enum ResetBehavior + { + /// + /// Clearing the list notifies a with a + /// . + /// + Reset, + + /// + /// Clearing the list notifies a with a + /// . + /// + Remove, + } + /// /// A notifying list. /// /// The type of the list items. /// + /// /// PerspexList is similar to - /// except that when the method is called, it notifies with a - /// action, passing the items that were - /// removed. + /// with a few added features: + /// + /// + /// + /// + /// It can be configured to notify the event with a + /// action instead of a + /// when the list is cleared by + /// setting to . + /// removed + /// + /// + /// A function can be used to validate each item before insertion. + /// removed + /// + /// /// public class PerspexList : IPerspexList, IList, INotifyCollectionChanged, INotifyPropertyChanged { @@ -65,6 +97,17 @@ namespace Perspex.Collections /// public int Count => _inner.Count; + /// + /// Gets or sets the reset behavior of the list. + /// + public ResetBehavior ResetBehavior { get; set; } + + /// + /// Gets or sets a validation routine that can be used to validate items before they are + /// added. + /// + public Action Validate { get; set; } + /// 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 /// The item. 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(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(); - NotifyRemove(old, 0); + NotifyReset(old); } /// @@ -204,6 +258,7 @@ namespace Perspex.Collections /// The item. 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(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(); } + + /// + /// Raises the event with a reset action. + /// + /// The items that were removed. + 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(); + } } } \ No newline at end of file diff --git a/src/Perspex.Controls/ColumnDefinitions.cs b/src/Perspex.Controls/ColumnDefinitions.cs index 3d8ca60181..2ac191ee66 100644 --- a/src/Perspex.Controls/ColumnDefinitions.cs +++ b/src/Perspex.Controls/ColumnDefinitions.cs @@ -17,6 +17,7 @@ namespace Perspex.Controls /// public ColumnDefinitions() { + ResetBehavior = ResetBehavior.Remove; } /// @@ -24,6 +25,7 @@ namespace Perspex.Controls /// /// A string representation of the column definitions. public ColumnDefinitions(string s) + : this() { AddRange(GridLength.ParseLengths(s, CultureInfo.InvariantCulture).Select(x => new ColumnDefinition(x))); } diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs index 443985f3eb..b48b4afca6 100644 --- a/src/Perspex.Controls/Control.cs +++ b/src/Perspex.Controls/Control.cs @@ -271,7 +271,9 @@ namespace Perspex.Controls { if (_logicalChildren == null) { - _logicalChildren = new PerspexList(); + var list = new PerspexList(); + list.ResetBehavior = ResetBehavior.Remove; + _logicalChildren = list; } return _logicalChildren; diff --git a/src/Perspex.Controls/Controls.cs b/src/Perspex.Controls/Controls.cs index a7435259da..16d84ab369 100644 --- a/src/Perspex.Controls/Controls.cs +++ b/src/Perspex.Controls/Controls.cs @@ -16,6 +16,7 @@ namespace Perspex.Controls /// public Controls() { + ResetBehavior = ResetBehavior.Remove; } /// @@ -25,6 +26,7 @@ namespace Perspex.Controls public Controls(IEnumerable items) : base(items) { + ResetBehavior = ResetBehavior.Remove; } } } diff --git a/src/Perspex.Controls/RowDefinitions.cs b/src/Perspex.Controls/RowDefinitions.cs index 7ebc6a8fb5..e8e418dafe 100644 --- a/src/Perspex.Controls/RowDefinitions.cs +++ b/src/Perspex.Controls/RowDefinitions.cs @@ -17,6 +17,7 @@ namespace Perspex.Controls /// public RowDefinitions() { + ResetBehavior = ResetBehavior.Remove; } /// @@ -24,6 +25,7 @@ namespace Perspex.Controls /// /// A string representation of the row definitions. public RowDefinitions(string s) + : this() { AddRange(GridLength.ParseLengths(s, CultureInfo.InvariantCulture).Select(x => new RowDefinition(x))); } diff --git a/src/Perspex.Controls/Templates/DataTemplates.cs b/src/Perspex.Controls/Templates/DataTemplates.cs index cc0bc24792..be9bd20e99 100644 --- a/src/Perspex.Controls/Templates/DataTemplates.cs +++ b/src/Perspex.Controls/Templates/DataTemplates.cs @@ -10,5 +10,12 @@ namespace Perspex.Controls.Templates /// public class DataTemplates : PerspexList { + /// + /// Initializes a new instance of the class. + /// + public DataTemplates() + { + ResetBehavior = ResetBehavior.Remove; + } } } \ No newline at end of file diff --git a/src/Perspex.SceneGraph/Visual.cs b/src/Perspex.SceneGraph/Visual.cs index 84940468ea..a63b8b0aad 100644 --- a/src/Perspex.SceneGraph/Visual.cs +++ b/src/Perspex.SceneGraph/Visual.cs @@ -124,6 +124,7 @@ namespace Perspex }); _visualChildren = new PerspexList(); + _visualChildren.ResetBehavior = ResetBehavior.Remove; _visualChildren.CollectionChanged += VisualChildrenChanged; } diff --git a/tests/Perspex.Base.UnitTests/Collections/PerspexListTests.cs b/tests/Perspex.Base.UnitTests/Collections/PerspexListTests.cs index 70509b9f0c..175395fd27 100644 --- a/tests/Perspex.Base.UnitTests/Collections/PerspexListTests.cs +++ b/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(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(new[] { 1, 2, 3 }); var raised = false; + target.ResetBehavior = ResetBehavior.Remove; target.CollectionChanged += (s, e) => { Assert.Equal(target, s);