From 9c936255bee884d103e3d203d26a4a2bef3cda92 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 20 Mar 2023 10:51:26 +0100 Subject: [PATCH] Ensure a changing value overrides current value. When a binding produces a new value, ensure that it overwrites any value in place from `SetCurrentValue` if the priority is the same. --- src/Avalonia.Base/PropertyStore/ValueStore.cs | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index 81206d3065..9fbe456ea1 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -395,7 +395,7 @@ namespace Avalonia.PropertyStore if (TryGetEffectiveValue(property, out var existing)) { if (priority <= existing.BasePriority) - ReevaluateEffectiveValue(property, existing); + ReevaluateEffectiveValue(property, existing, changedValueEntry: entry); } else { @@ -774,6 +774,7 @@ namespace Avalonia.PropertyStore private void ReevaluateEffectiveValue( AvaloniaProperty property, EffectiveValue? current, + IValueEntry? changedValueEntry = null, bool ignoreLocalValue = false) { ++_isEvaluating; @@ -803,21 +804,11 @@ namespace Avalonia.PropertyStore // effective values of all properties. if (activeChanged && frame.EntryCount > 1) { - ReevaluateEffectiveValues(); + ReevaluateEffectiveValues(changedValueEntry); return; } - // We're interested in the value if: - // - There is no current effective value, or - // - The value's priority is higher than the current effective value's priority, or - // - The value's priority is equal to the current effective value's priority, but the effective - // value was set via SetCurrentValue, or - // - The value is a non-animation value and its priority is higher than the current - // effective value's base priority - var isRelevantPriority = current is null || - (priority < current.Priority && priority < current.BasePriority) || - (priority == current.Priority && current.IsOverridenCurrentValue) || - (priority > BindingPriority.Animation && priority < current.BasePriority); + var isRelevantPriority = HasHigherPriority(entry!, priority, current, changedValueEntry); if (foundEntry && isRelevantPriority && entry!.HasValue) { @@ -862,7 +853,7 @@ namespace Avalonia.PropertyStore } } - private void ReevaluateEffectiveValues() + private void ReevaluateEffectiveValues(IValueEntry? changedValueEntry = null) { ++_isEvaluating; @@ -897,10 +888,9 @@ namespace Avalonia.PropertyStore { var entry = frame.GetEntry(j); var property = entry.Property; - - // Skip if we already have a value/base value for this property. - if (_effectiveValues.TryGetValue(property, out var effectiveValue) && - effectiveValue.BasePriority < BindingPriority.Unset) + _effectiveValues.TryGetValue(property, out var effectiveValue); + + if (!HasHigherPriority(entry, priority, effectiveValue, changedValueEntry)) continue; if (!entry.HasValue) @@ -945,6 +935,37 @@ namespace Avalonia.PropertyStore } } + private static bool HasHigherPriority( + IValueEntry entry, + BindingPriority entryPriority, + EffectiveValue? current, + IValueEntry? changedValueEntry) + { + // Set the value if: there is no current effective value; or + if (current is null) + return true; + + // The value's priority is higher than the current effective value's priority; or + if (entryPriority < current.Priority && entryPriority < current.BasePriority) + return true; + + // - The value's priority is equal to the current effective value's priority + // - But the effective value was set via SetCurrentValue + // - As long as the SetCurrentValue wasn't overriding the value from the value entry under consideration + // - Or if it was, the value entry under consideration has changed; or + if (entryPriority == current.Priority && + current.IsOverridenCurrentValue && + (current.ValueEntry != entry || entry == changedValueEntry)) + return true; + + // The value is a non-animation value and its priority is higher than the current effective value's base + // priority. + if (entryPriority > BindingPriority.Animation && entryPriority < current.BasePriority) + return true; + + return false; + } + private bool TryGetEffectiveValue( AvaloniaProperty property, [NotNullWhen(true)] out EffectiveValue? value)