Browse Source

Clear data validation when binding completes.

pull/10423/head
Steven Kirk 3 years ago
parent
commit
40ab20146f
  1. 11
      src/Avalonia.Base/PropertyStore/DirectBindingObserver.cs
  2. 5
      src/Avalonia.Base/PropertyStore/DirectUntypedBindingObserver.cs
  3. 9
      src/Avalonia.Base/PropertyStore/LocalValueBindingObserverBase.cs
  4. 42
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs
  5. 24
      tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs

11
src/Avalonia.Base/PropertyStore/DirectBindingObserver.cs

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

5
src/Avalonia.Base/PropertyStore/DirectUntypedBindingObserver.cs

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

9
src/Avalonia.Base/PropertyStore/LocalValueBindingObserverBase.cs

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

42
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_DataValidation.cs

@ -92,6 +92,48 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(1, target.Notifications.Count); 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 GetProperty();
protected abstract T GetNonValidatedProperty(); protected abstract T GetNonValidatedProperty();
} }

24
tests/Avalonia.Markup.UnitTests/Data/BindingTests_DataValidation.cs

@ -67,6 +67,30 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Null(target.DataValidationError); 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(); private protected abstract (DataValidationTestControl, T) CreateTarget();
} }

Loading…
Cancel
Save