Browse Source

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.
pull/10695/head
Steven Kirk 3 years ago
parent
commit
9c936255be
  1. 57
      src/Avalonia.Base/PropertyStore/ValueStore.cs

57
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)

Loading…
Cancel
Save