diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 83b0d4e533..be7a14dcf9 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -464,7 +464,7 @@ namespace Avalonia /// void IPriorityValueOwner.BindingNotificationReceived(PriorityValue sender, BindingNotification notification) { - BindingNotificationReceived(sender.Property, notification); + UpdateDataValidation(sender.Property, notification); } /// @@ -499,14 +499,16 @@ namespace Avalonia } /// - /// Occurs when a is received for a property which has - /// data validation enabled. + /// Called to update the validation state for properties for which data validation is + /// enabled. /// /// The property. - /// The binding notification. - protected virtual void BindingNotificationReceived( + /// + /// The new validation status. A value of null indicates no validation error. + /// + protected virtual void UpdateDataValidation( AvaloniaProperty property, - BindingNotification notification) + BindingNotification status) { } @@ -647,20 +649,12 @@ namespace Avalonia /// private void DirectBindingSet(AvaloniaProperty property, object value) { + var validated = property.GetMetadata(GetType()).EnableDataValidation; var notification = value as BindingNotification; - if (notification == null) - { - SetValue(property, value); - } - else + if (notification != null) { - if (notification.HasValue) - { - SetValue(property, notification.Value); - } - - BindingNotificationReceived(property, notification); + value = notification.Value; if (notification.ErrorType == BindingErrorType.Error) { @@ -673,6 +667,16 @@ namespace Avalonia ExceptionUtilities.GetMessage(notification.Error)); } } + + if (notification?.HasValue != false) + { + SetValue(property, value); + } + + if (validated) + { + UpdateDataValidation(property, notification); + } } /// diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/DataValidatiorBase.cs b/src/Markup/Avalonia.Markup/Data/Plugins/DataValidatiorBase.cs index 95d269f437..9b387456ca 100644 --- a/src/Markup/Avalonia.Markup/Data/Plugins/DataValidatiorBase.cs +++ b/src/Markup/Avalonia.Markup/Data/Plugins/DataValidatiorBase.cs @@ -68,13 +68,11 @@ namespace Avalonia.Markup.Data.Plugins /// /// The value. /// - /// Notifies the observer that the value has changed. The value will be wrapped in a - /// if it is not already a binding notification. + /// Notifies the observer that the value has changed. /// protected virtual void InnerValueChanged(object value) { - var notification = value as BindingNotification ?? new BindingNotification(value); - Observer.OnNext(notification); + Observer.OnNext(value); } } } \ No newline at end of file diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/IndeiValidationPlugin.cs b/src/Markup/Avalonia.Markup/Data/Plugins/IndeiValidationPlugin.cs index 8fb2568f30..561bd66db3 100644 --- a/src/Markup/Avalonia.Markup/Data/Plugins/IndeiValidationPlugin.cs +++ b/src/Markup/Avalonia.Markup/Data/Plugins/IndeiValidationPlugin.cs @@ -40,7 +40,7 @@ namespace Avalonia.Markup.Data.Plugins { if (e.PropertyName == _name || string.IsNullOrEmpty(e.PropertyName)) { - Observer.OnNext(CreateBindingNotification(Value)); + Observer.OnNext(BuildResult(Value)); } } @@ -76,10 +76,10 @@ namespace Avalonia.Markup.Data.Plugins protected override void InnerValueChanged(object value) { - base.InnerValueChanged(CreateBindingNotification(value)); + base.InnerValueChanged(BuildResult(value)); } - private BindingNotification CreateBindingNotification(object value) + private object BuildResult(object value) { var target = (INotifyDataErrorInfo)_reference.Target; @@ -98,7 +98,7 @@ namespace Avalonia.Markup.Data.Plugins } } - return new BindingNotification(value); + return value; } private Exception GenerateException(IList errors) diff --git a/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs b/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs index cdcaeda4cc..a363d69486 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_DataValidation.cs @@ -81,16 +81,16 @@ namespace Avalonia.Markup.UnitTests.Data observer.SetValue("foo"); observer.SetValue(5); - Assert.Equal(new[] + Assert.Equal(new object[] { - new BindingNotification(0), + 0, // Value is notified twice as ErrorsChanged is always called by IndeiTest. - new BindingNotification(5), - new BindingNotification(5), + 5, + 5, // Value is first signalled without an error as validation hasn't been updated. - new BindingNotification(-5), + -5, new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, -5), // Exception is thrown by trying to set value to "foo". @@ -100,7 +100,7 @@ namespace Avalonia.Markup.UnitTests.Data // Value is set then validation is updated. new BindingNotification(new Exception("Must be positive"), BindingErrorType.DataValidationError, 5), - new BindingNotification(5), + 5, }, result); } diff --git a/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs b/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs index 3263aaace2..3b915f97a8 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_Observable.cs @@ -85,7 +85,7 @@ namespace Avalonia.Markup.UnitTests.Data data.Next.OnNext(new Class2("foo")); sync.ExecutePostedCallbacks(); - Assert.Equal(new[] { new BindingNotification("foo") }, result); + Assert.Equal(new[] { "foo" }, result); sub.Dispose(); Assert.Equal(0, data.PropertyChangedSubscriptionCount); diff --git a/tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs b/tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs index 5498926fe4..16b71c87fe 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/ExpressionSubjectTests.cs @@ -288,11 +288,11 @@ namespace Avalonia.Markup.UnitTests.Data target.OnNext("bar"); Assert.Equal( - new[] + new object[] { - new BindingNotification("5.6"), - new BindingNotification("1.2"), - new BindingNotification("3.4"), + "5.6", + "1.2", + "3.4", new BindingNotification( new InvalidCastException("Error setting 'DoubleValue': Could not convert 'bar' to 'System.Double'"), BindingErrorType.Error) diff --git a/tests/Avalonia.Markup.UnitTests/Data/Plugins/ExceptionValidationPluginTests.cs b/tests/Avalonia.Markup.UnitTests/Data/Plugins/ExceptionValidationPluginTests.cs index 4a34791008..7b7128ed2f 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/Plugins/ExceptionValidationPluginTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/Plugins/ExceptionValidationPluginTests.cs @@ -14,7 +14,7 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins public class ExceptionValidationPluginTests { [Fact] - public void Produces_BindingNotifications() + public void Produces_Correct_Results() { var inpcAccessorPlugin = new InpcPropertyAccessorPlugin(); var validatorPlugin = new ExceptionValidationPlugin(); @@ -28,12 +28,12 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins validator.SetValue(-2, BindingPriority.LocalValue); validator.SetValue(6, BindingPriority.LocalValue); - Assert.Equal(new[] + Assert.Equal(new object[] { - new BindingNotification(0), - new BindingNotification(5), + 0, + 5, new BindingNotification(new ArgumentOutOfRangeException("value"), BindingErrorType.DataValidationError), - new BindingNotification(6), + 6, }, result); } diff --git a/tests/Avalonia.Markup.UnitTests/Data/Plugins/IndeiValidationPluginTests.cs b/tests/Avalonia.Markup.UnitTests/Data/Plugins/IndeiValidationPluginTests.cs index 788bc25a34..9e6657e0a1 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/Plugins/IndeiValidationPluginTests.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/Plugins/IndeiValidationPluginTests.cs @@ -14,7 +14,7 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins public class IndeiValidationPluginTests { [Fact] - public void Produces_BindingNotifications() + public void Produces_Correct_Results() { var inpcAccessorPlugin = new InpcPropertyAccessorPlugin(); var validatorPlugin = new IndeiValidationPlugin(); @@ -29,19 +29,19 @@ namespace Avalonia.Markup.UnitTests.Data.Plugins data.Maximum = 10; data.Maximum = 5; - Assert.Equal(new[] + Assert.Equal(new object[] { - new BindingNotification(0), - new BindingNotification(5), + 0, + 5, // Value is first signalled without an error as validation hasn't been updated. - new BindingNotification(6), + 6, // Then the ErrorsChanged event is fired. new BindingNotification(new Exception("Must be less than Maximum"), BindingErrorType.DataValidationError, 6), // Maximum is changed to 10 so value is now valid. - new BindingNotification(6), + 6, // And Maximum is changed back to 5. new BindingNotification(new Exception("Must be less than Maximum"), BindingErrorType.DataValidationError, 6), diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs index 8759cb42c5..fd15d97fbf 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Validation.cs @@ -45,10 +45,10 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data Assert.Equal( new[] { - new BindingNotification(5), - new BindingNotification(6), + null, // 5 + null, // 6 new BindingNotification(new ArgumentOutOfRangeException("value"), BindingErrorType.DataValidationError), - new BindingNotification(7), + null, // 7 }, target.Notifications.AsEnumerable()); } @@ -94,7 +94,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data public IList Notifications { get; } = new List(); - protected override void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification) + protected override void UpdateDataValidation(AvaloniaProperty property, BindingNotification notification) { Notifications.Add(notification); }