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);
}
}
}