Browse Source

Only send BindingNotifications on error.

Assume that a normal value when received by a property with data
validation enabled means no error.
Steven Kirk 9 years ago
parent
commit
57e646583f

+ 21 - 17
src/Avalonia.Base/AvaloniaObject.cs

@@ -464,7 +464,7 @@ namespace Avalonia
         /// <inheritdoc/>
         void IPriorityValueOwner.BindingNotificationReceived(PriorityValue sender, BindingNotification notification)
         {
-            BindingNotificationReceived(sender.Property, notification);
+            UpdateDataValidation(sender.Property, notification);
         }
 
         /// <inheritdoc/>
@@ -499,14 +499,16 @@ namespace Avalonia
         }
 
         /// <summary>
-        /// Occurs when a <see cref="BindingNotification"/> is received for a property which has
-        /// data validation enabled.
+        /// Called to update the validation state for properties for which data validation is
+        /// enabled.
         /// </summary>
         /// <param name="property">The property.</param>
-        /// <param name="notification">The binding notification.</param>
-        protected virtual void BindingNotificationReceived(
+        /// <param name="status">
+        /// The new validation status. A value of null indicates no validation error.
+        /// </param>
+        protected virtual void UpdateDataValidation(
             AvaloniaProperty property,
-            BindingNotification notification)
+            BindingNotification status)
         {
         }
 
@@ -647,20 +649,12 @@ namespace Avalonia
         /// <returns></returns>
         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);
+            }
         }
 
         /// <summary>

+ 2 - 4
src/Markup/Avalonia.Markup/Data/Plugins/DataValidatiorBase.cs

@@ -68,13 +68,11 @@ namespace Avalonia.Markup.Data.Plugins
         /// </summary>
         /// <param name="value">The value.</param>
         /// <remarks>
-        /// Notifies the observer that the value has changed. The value will be wrapped in a
-        /// <see cref="BindingNotification"/> if it is not already a binding notification.
+        /// Notifies the observer that the value has changed.
         /// </remarks>
         protected virtual void InnerValueChanged(object value)
         {
-            var notification = value as BindingNotification ?? new BindingNotification(value);
-            Observer.OnNext(notification);
+            Observer.OnNext(value);
         }
     }
 }

+ 4 - 4
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<string> errors)

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

+ 1 - 1
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);

+ 4 - 4
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)

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

+ 6 - 6
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),

+ 4 - 4
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<BindingNotification> Notifications { get; } = new List<BindingNotification>();
 
-            protected override void BindingNotificationReceived(AvaloniaProperty property, BindingNotification notification)
+            protected override void UpdateDataValidation(AvaloniaProperty property, BindingNotification notification)
             {
                 Notifications.Add(notification);
             }