Browse Source

Clear data validation when binding completes.

Steven Kirk 2 years ago
parent
commit
40ab20146f

+ 10 - 1
src/Avalonia.Base/PropertyStore/DirectBindingObserver.cs

@@ -9,11 +9,13 @@ namespace Avalonia.PropertyStore
         IDisposable
     {
         private readonly ValueStore _owner;
+        private readonly bool _hasDataValidation;
         private IDisposable? _subscription;
 
         public DirectBindingObserver(ValueStore owner, DirectPropertyBase<T> property)
         {
             _owner = owner;
+            _hasDataValidation = property.GetMetadata(owner.Owner.GetType())?.EnableDataValidation ?? false;
             Property = property;
         }
 
@@ -33,10 +35,17 @@ namespace Avalonia.PropertyStore
         {
             _subscription?.Dispose();
             _subscription = null;
+            OnCompleted();
+        }
+
+        public void OnCompleted()
+        {
             _owner.OnLocalValueBindingCompleted(Property, this);
+
+            if (_hasDataValidation)
+                _owner.Owner.OnUpdateDataValidation(Property, BindingValueType.UnsetValue, null);
         }
 
-        public void OnCompleted() => _owner.OnLocalValueBindingCompleted(Property, this);
         public void OnError(Exception error) => OnCompleted();
 
         public void OnNext(T value)

+ 5 - 0
src/Avalonia.Base/PropertyStore/DirectUntypedBindingObserver.cs

@@ -10,11 +10,13 @@ namespace Avalonia.PropertyStore
         IDisposable
     {
         private readonly ValueStore _owner;
+        private readonly bool _hasDataValidation;
         private IDisposable? _subscription;
 
         public DirectUntypedBindingObserver(ValueStore owner, DirectPropertyBase<T> property)
         {
             _owner = owner;
+            _hasDataValidation = property.GetMetadata(owner.Owner.GetType())?.EnableDataValidation ?? false;
             Property = property;
         }
 
@@ -30,6 +32,9 @@ namespace Avalonia.PropertyStore
             _subscription?.Dispose();
             _subscription = null;
             _owner.OnLocalValueBindingCompleted(Property, this);
+
+            if (_hasDataValidation)
+                _owner.Owner.OnUpdateDataValidation(Property, BindingValueType.UnsetValue, null);
         }
 
         public void OnCompleted() => _owner.OnLocalValueBindingCompleted(Property, this);

+ 8 - 1
src/Avalonia.Base/PropertyStore/LocalValueBindingObserverBase.cs

@@ -37,10 +37,17 @@ namespace Avalonia.PropertyStore
         {
             _subscription?.Dispose();
             _subscription = null;
+            OnCompleted();
+        }
+
+        public void OnCompleted()
+        {
+            if (_hasDataValidation)
+                _owner.Owner.OnUpdateDataValidation(Property, BindingValueType.UnsetValue, null);
+
             _owner.OnLocalValueBindingCompleted(Property, this);
         }
 
-        public void OnCompleted() => _owner.OnLocalValueBindingCompleted(Property, this);
         public void OnError(Exception error) => OnCompleted();
 
         public void OnNext(T value)

+ 42 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs

@@ -92,6 +92,48 @@ namespace Avalonia.Base.UnitTests
                 Assert.Equal(1, target.Notifications.Count);
             }
 
+            [Fact]
+            public void Disposing_Binding_Subscription_Clears_DataValidation()
+            {
+                var target = new Class1();
+                var source = new Subject<BindingValue<int>>();
+                var property = GetProperty();
+                var error = new Exception();
+                var sub = target.Bind(property, source);
+
+                source.OnNext(6);
+                source.OnNext(BindingValue<int>.DataValidationError(error));
+                sub.Dispose();
+
+                Assert.Equal(new Notification[]
+                {
+                    new(BindingValueType.Value, 6, null),
+                    new(BindingValueType.DataValidationError, 6, error),
+                    new(BindingValueType.UnsetValue, 6, null),
+                }, target.Notifications);
+            }
+
+            [Fact]
+            public void Completing_Binding_Clears_DataValidation()
+            {
+                var target = new Class1();
+                var source = new Subject<BindingValue<int>>();
+                var property = GetProperty();
+                var error = new Exception();
+                
+                target.Bind(property, source);
+                source.OnNext(6);
+                source.OnNext(BindingValue<int>.DataValidationError(error));
+                source.OnCompleted();
+
+                Assert.Equal(new Notification[]
+                {
+                    new(BindingValueType.Value, 6, null),
+                    new(BindingValueType.DataValidationError, 6, error),
+                    new(BindingValueType.UnsetValue, 6, null),
+                }, target.Notifications);
+            }
+
             protected abstract T GetProperty();
             protected abstract T GetNonValidatedProperty();
         }

+ 24 - 0
tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs

@@ -67,6 +67,30 @@ namespace Avalonia.Markup.UnitTests.Data
                 Assert.Null(target.DataValidationError);
             }
 
+            [Fact]
+            public void Disposing_Binding_Subscription_Clears_DataValidation()
+            {
+                var (target, property) = CreateTarget();
+                var binding = new Binding(nameof(ExceptionValidatingModel.Value))
+                {
+                    Mode = BindingMode.TwoWay
+                };
+
+                target.DataContext = new IndeiValidatingModel
+                {
+                    Value = 200,
+                };
+                
+                var sub = target.Bind(property, binding);
+
+                Assert.Equal(200, target.GetValue(property));
+                Assert.IsType<DataValidationException>(target.DataValidationError);
+
+                sub.Dispose();
+
+                Assert.Null(target.DataValidationError);
+            }
+
             private protected abstract (DataValidationTestControl, T) CreateTarget();
         }