diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs index 38c1728cd9..3e17a81dd8 100644 --- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs +++ b/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); diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs index 9ece2b8042..8a8e9ad153 100644 --- a/src/Avalonia.Base/ValueStore.cs +++ b/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(_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(_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(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); } }