Browse Source

Fix more batch update problems.

Fixes tests in previous commit which test scenarios where bindings are completed/disposed during and when ending batch updates.
pull/5686/head
Steven Kirk 5 years ago
parent
commit
d2c38a8526
  1. 9
      src/Avalonia.Base/PropertyStore/BindingEntry.cs
  2. 19
      src/Avalonia.Base/ValueStore.cs

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

@ -65,7 +65,6 @@ namespace Avalonia.PropertyStore
{
_subscription?.Dispose();
_subscription = null;
_isSubscribed = false;
OnCompleted();
}
@ -74,6 +73,7 @@ namespace Avalonia.PropertyStore
var oldValue = _value;
_value = default;
Priority = BindingPriority.Unset;
_isSubscribed = false;
_sink.Completed(Property, this, oldValue);
}
@ -104,8 +104,11 @@ namespace Avalonia.PropertyStore
public void Start(bool ignoreBatchUpdate)
{
// We can't use _subscription to check whether we're subscribed because it won't be set
// until Subscribe has finished, which will be too late to prevent reentrancy.
if (!_isSubscribed && (!_batchUpdate || ignoreBatchUpdate))
// until Subscribe has finished, which will be too late to prevent reentrancy. In addition
// don't re-subscribe to completed/disposed bindings (indicated by Unset priority).
if (!_isSubscribed &&
Priority != BindingPriority.Unset &&
(!_batchUpdate || ignoreBatchUpdate))
{
_isSubscribed = true;
_subscription = Source.Subscribe(this);

19
src/Avalonia.Base/ValueStore.cs

@ -173,7 +173,7 @@ namespace Avalonia
// During batch update values can't be removed immediately because they're needed to raise
// a correctly-typed _sink.ValueChanged notification. They instead mark themselves for removal
// by setting their priority to Unset.
if (_batchUpdate is null)
if (!IsBatchUpdating())
{
_values.Remove(property);
}
@ -285,7 +285,7 @@ namespace Avalonia
else
{
var priorityValue = new PriorityValue<T>(_owner, property, this, l);
if (_batchUpdate is object)
if (IsBatchUpdating())
priorityValue.BeginBatchUpdate();
result = priorityValue.SetValue(value, priority);
_values.SetValue(property, priorityValue);
@ -311,7 +311,7 @@ namespace Avalonia
{
priorityValue = new PriorityValue<T>(_owner, property, this, e);
if (_batchUpdate is object)
if (IsBatchUpdating())
{
priorityValue.BeginBatchUpdate();
}
@ -338,7 +338,7 @@ namespace Avalonia
private void AddValue(AvaloniaProperty property, IValue value)
{
_values.AddValue(property, value);
if (_batchUpdate?.IsBatchUpdating == true && value is IBatchUpdate batch)
if (IsBatchUpdating() && value is IBatchUpdate batch)
batch.BeginBatchUpdate();
value.Start();
}
@ -364,6 +364,8 @@ namespace Avalonia
}
}
private bool IsBatchUpdating() => _batchUpdate?.IsBatchUpdating == true;
private static bool IsRemoveSentinel<T>(IValue value)
{
// Local value entries are optimized and contain only a single value field to save space,
@ -447,9 +449,14 @@ namespace Avalonia
// During batch update values can't be removed immediately because they're needed to raise
// the _sink.ValueChanged notification. They instead mark themselves for removal by setting
// their priority to Unset.
if (slot.Priority == BindingPriority.Unset)
// their priority to Unset. We need to re-read the slot here because raising ValueChanged
// could have caused it to be updated.
if (values.TryGetValue(entry.property, out var updatedSlot) &&
updatedSlot.Priority == BindingPriority.Unset)
{
if (entry.property.Name == "Transitions" && _owner._owner.GetType().Name == "TabItem")
{
}
values.Remove(entry.property);
}
}

Loading…
Cancel
Save