Browse Source

Handle batching binding completion.

pull/5070/head
Steven Kirk 6 years ago
parent
commit
c4c4cc8bd8
  1. 12
      src/Avalonia.Base/PropertyStore/BindingEntry.cs
  2. 11
      src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs
  3. 16
      src/Avalonia.Base/ValueStore.cs
  4. 41
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs

12
src/Avalonia.Base/PropertyStore/BindingEntry.cs

@ -42,7 +42,7 @@ namespace Avalonia.PropertyStore
}
public StyledPropertyBase<T> Property { get; }
public BindingPriority Priority { get; }
public BindingPriority Priority { get; private set; }
public IObservable<BindingValue<T>> Source { get; }
Optional<object> IValue.GetValue() => _value.ToObject();
@ -66,10 +66,16 @@ namespace Avalonia.PropertyStore
_subscription?.Dispose();
_subscription = null;
_isSubscribed = false;
_sink.Completed(Property, this, _value);
OnCompleted();
}
public void OnCompleted() => _sink.Completed(Property, this, _value);
public void OnCompleted()
{
var oldValue = _value;
_value = default;
Priority = BindingPriority.Unset;
_sink.Completed(Property, this, oldValue);
}
public void OnError(Exception error)
{

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

@ -28,7 +28,7 @@ namespace Avalonia.PropertyStore
}
public StyledPropertyBase<T> Property { get; }
public BindingPriority Priority { get; }
public BindingPriority Priority { get; private set; }
Optional<object> IValue.GetValue() => _value.ToObject();
public Optional<T> GetValue(BindingPriority maxPriority = BindingPriority.Animation)
@ -36,7 +36,14 @@ namespace Avalonia.PropertyStore
return Priority >= maxPriority ? _value : Optional<T>.Empty;
}
public void Dispose() => _sink.Completed(Property, this, _value);
public void Dispose()
{
var oldValue = _value;
_value = default;
Priority = BindingPriority.Unset;
_sink.Completed(Property, this, oldValue);
}
public void Reparent(IValueSink sink) => _sink = sink;
public void Start() { }

16
src/Avalonia.Base/ValueStore.cs

@ -67,11 +67,15 @@ namespace Avalonia
if (_values.TryGetValue(entry.property, out var slot))
{
slot.RaiseValueChanged(_sink, _owner, entry.property, entry.oldValue);
if (slot.Priority == BindingPriority.Unset)
{
_values.Remove(entry.property);
}
}
else
{
// TODO
throw new NotImplementedException();
throw new AvaloniaInternalException("Value could not be found at the end of batch update.");
}
}
@ -249,13 +253,17 @@ namespace Avalonia
IPriorityValueEntry entry,
Optional<T> oldValue)
{
if (_values.TryGetValue(property, out var slot))
if (_values.TryGetValue(property, out var slot) && slot == entry)
{
if (slot == entry)
if (_batchUpdate is null)
{
_values.Remove(property);
_sink.Completed(property, entry, oldValue);
}
else
{
NotifyValueChanged(property, oldValue, default, BindingPriority.Unset);
}
}
}

41
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs

@ -38,6 +38,21 @@ namespace Avalonia.Base.UnitTests
Assert.Empty(raised);
}
[Fact]
public void Binding_Completion_Should_Not_Raise_Property_Changes_During_Batch_Update()
{
var target = new TestClass();
var observable = new TestObservable<string>("foo");
var raised = new List<string>();
target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x));
target.BeginBatchUpdate();
observable.OnCompleted();
Assert.Empty(raised);
}
[Fact]
public void SetValue_Change_Should_Be_Raised_After_Batch_Update_1()
{
@ -204,6 +219,27 @@ namespace Avalonia.Base.UnitTests
Assert.Equal("baz", raised[0].NewValue);
}
[Fact]
public void Binding_Completion_Should_Be_Raised_After_Batch_Update()
{
var target = new TestClass();
var observable = new TestObservable<string>("foo");
var raised = new List<AvaloniaPropertyChangedEventArgs>();
target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue);
target.PropertyChanged += (s, e) => raised.Add(e);
target.BeginBatchUpdate();
observable.OnCompleted();
target.EndBatchUpdate();
Assert.Equal(1, raised.Count);
Assert.Null(target.Foo);
Assert.Equal("foo", raised[0].OldValue);
Assert.Null(raised[0].NewValue);
Assert.Equal(BindingPriority.Unset, raised[0].Priority);
}
[Fact]
public void Bindings_Should_Be_Subscribed_Before_Batch_Update()
{
@ -325,14 +361,19 @@ namespace Avalonia.Base.UnitTests
public class TestObservable<T> : ObservableBase<BindingValue<T>>
{
private readonly T _value;
private IObserver<BindingValue<T>> _observer;
public TestObservable(T value) => _value = value;
public int SubscribeCount { get; private set; }
public void OnCompleted() => _observer.OnCompleted();
public void OnError(Exception e) => _observer.OnError(e);
protected override IDisposable SubscribeCore(IObserver<BindingValue<T>> observer)
{
++SubscribeCount;
_observer = observer;
observer.OnNext(_value);
return Disposable.Empty;
}

Loading…
Cancel
Save