From aab4331a52e209ec48fd1ed796b224399efeda6e Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 19 Apr 2016 16:40:31 -0500 Subject: [PATCH] Changed validation on controls to allow notifications from multiple types of validation and correctly report if it is valid. Changed initalization of validation plugins to nest within each other so there can be multiple active at once. --- .../Perspex.Markup/Data/ExpressionNode.cs | 10 ++++++- .../Data/PropertyAccessorNode.cs | 8 +++--- src/Perspex.Controls/Control.cs | 10 +++---- .../ControlValidationStatus.cs | 27 +++++++++++++++++++ src/Perspex.Controls/Perspex.Controls.csproj | 1 + .../Perspex.Controls.UnitTests.csproj | 3 --- .../Data/BindingTests_Validation.cs | 9 +++---- 7 files changed, 51 insertions(+), 17 deletions(-) create mode 100644 src/Perspex.Controls/ControlValidationStatus.cs diff --git a/src/Markup/Perspex.Markup/Data/ExpressionNode.cs b/src/Markup/Perspex.Markup/Data/ExpressionNode.cs index a120aa8901..ac1cad5b61 100644 --- a/src/Markup/Perspex.Markup/Data/ExpressionNode.cs +++ b/src/Markup/Perspex.Markup/Data/ExpressionNode.cs @@ -107,7 +107,15 @@ namespace Perspex.Markup.Data protected virtual void SendValidationStatus(ValidationStatus status) { - _subject?.OnNext(status); + //Even if elements only bound to sub-values, send validation changes along so they will be surfaced to the UI level. + if (_subject != null) + { + _subject.OnNext(status); + } + else + { + Next.SendValidationStatus(status); + } } protected virtual void Unsubscribe(object target) diff --git a/src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs b/src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs index 72cf80e79e..e0561ecfcf 100644 --- a/src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs +++ b/src/Markup/Perspex.Markup/Data/PropertyAccessorNode.cs @@ -55,10 +55,12 @@ namespace Perspex.Markup.Data if (accessorPlugin != null) { _accessor = accessorPlugin.Start(reference, PropertyName, SetCurrentValue); - var validationPlugin = ExpressionObserver.ValidationCheckers.FirstOrDefault(x => x.Match(reference)); - if (validationPlugin != null) + foreach (var validationPlugin in ExpressionObserver.ValidationCheckers.Where(x => x.Match(reference))) { - _accessor = validationPlugin.Start(reference, PropertyName, _accessor, SendValidationStatus); + if (validationPlugin != null) + { + _accessor = validationPlugin.Start(reference, PropertyName, _accessor, SendValidationStatus); + } } if (_accessor != null) diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs index f59811b79c..601975a19e 100644 --- a/src/Perspex.Controls/Control.cs +++ b/src/Perspex.Controls/Control.cs @@ -89,8 +89,8 @@ namespace Perspex.Controls /// /// Defines the property. /// - public static readonly DirectProperty ValidationStatusProperty = - PerspexProperty.RegisterDirect(nameof(ValidationStatus), c=> c.ValidationStatus); + public static readonly DirectProperty ValidationStatusProperty = + PerspexProperty.RegisterDirect(nameof(ValidationStatus), c=> c.ValidationStatus); private int _initCount; private string _name; @@ -406,12 +406,12 @@ namespace Perspex.Controls /// protected IPseudoClasses PseudoClasses => Classes; - private ValidationStatus validationStatus; + private ControlValidationStatus validationStatus = new ControlValidationStatus(); /// /// The current validation status of the control. /// - public ValidationStatus ValidationStatus + public ControlValidationStatus ValidationStatus { get { @@ -427,7 +427,7 @@ namespace Perspex.Controls protected override void DataValidation(PerspexProperty property, ValidationStatus status) { base.DataValidation(property, status); - ValidationStatus = status; + ValidationStatus.UpdateValidationStatus(status); } /// diff --git a/src/Perspex.Controls/ControlValidationStatus.cs b/src/Perspex.Controls/ControlValidationStatus.cs new file mode 100644 index 0000000000..aad65a1a0d --- /dev/null +++ b/src/Perspex.Controls/ControlValidationStatus.cs @@ -0,0 +1,27 @@ +using Perspex.Data; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Controls +{ + public class ControlValidationStatus : ValidationStatus, INotifyPropertyChanged + { + private Dictionary propertyValidation = new Dictionary(); + + public override bool IsValid => propertyValidation.Values.All(status => status.IsValid); + + public event PropertyChangedEventHandler PropertyChanged; + + public override bool Match(ValidationMethods enabledMethods) => true; + + public void UpdateValidationStatus(ValidationStatus status) + { + propertyValidation[status.GetType()] = status; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("")); + } + } +} diff --git a/src/Perspex.Controls/Perspex.Controls.csproj b/src/Perspex.Controls/Perspex.Controls.csproj index 8c62f1d66c..3bf7b4be9c 100644 --- a/src/Perspex.Controls/Perspex.Controls.csproj +++ b/src/Perspex.Controls/Perspex.Controls.csproj @@ -46,6 +46,7 @@ + diff --git a/tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj b/tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj index 932af42724..47635bf006 100644 --- a/tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj +++ b/tests/Perspex.Controls.UnitTests/Perspex.Controls.UnitTests.csproj @@ -196,9 +196,6 @@ - - - diff --git a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs index 799486bd5f..551f930406 100644 --- a/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs +++ b/tests/Perspex.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs @@ -13,7 +13,6 @@ namespace Perspex.Markup.Xaml.UnitTests.Data { public class BindingTests_Validation { - public class Data : INotifyPropertyChanged { private string mustbeNonEmpty; @@ -54,7 +53,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data target.Text = ""; - Assert.Null(target.ValidationStatus); + Assert.True(target.ValidationStatus.IsValid); } [Fact] @@ -71,7 +70,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data target.Bind(TextBlock.TextProperty, binding); target.Text = ""; - Assert.NotNull(target.ValidationStatus); + Assert.False(target.ValidationStatus.IsValid); } [Fact] @@ -89,7 +88,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data target.Tag = ""; - Assert.Null(target.ValidationStatus); + Assert.True(target.ValidationStatus.IsValid); } [Fact] @@ -106,7 +105,7 @@ namespace Perspex.Markup.Xaml.UnitTests.Data target.Bind(Control.TagProperty, binding); target.Tag = ""; - Assert.NotNull(target.ValidationStatus); + Assert.False(target.ValidationStatus.IsValid); } } }