Browse Source

Make EffectiveValue responsible for unsubscribing.

pull/8600/head
Steven Kirk 4 years ago
parent
commit
bd357e08dd
  1. 2
      src/Avalonia.Base/PropertyStore/BindingEntryBase.cs
  2. 103
      src/Avalonia.Base/PropertyStore/EffectiveValue.cs
  3. 208
      src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
  4. 2
      src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs
  5. 193
      src/Avalonia.Base/PropertyStore/ValueStore.cs

2
src/Avalonia.Base/PropertyStore/BindingEntryBase.cs

@ -143,7 +143,7 @@ namespace Avalonia.PropertyStore
_value = value.Value;
_hasValue = true;
if (_subscription is not null && _subscription != s_creatingQuiet)
Frame.Owner?.OnBindingValueChanged(Property, Frame.Priority, value.Value);
Frame.Owner?.OnBindingValueChanged(this, Frame.Priority);
}
}
else if (value.Type != BindingValueType.DoNothing)

103
src/Avalonia.Base/PropertyStore/EffectiveValue.cs

@ -1,5 +1,4 @@
using System;
using Avalonia.Data;
using Avalonia.Data;
namespace Avalonia.PropertyStore
{
@ -11,6 +10,9 @@ namespace Avalonia.PropertyStore
/// </remarks>
internal abstract class EffectiveValue
{
private IValueEntry? _valueEntry;
private IValueEntry? _baseValueEntry;
/// <summary>
/// Gets the current effective value as a boxed value.
/// </summary>
@ -33,52 +35,66 @@ namespace Avalonia.PropertyStore
public BindingPriority BasePriority { get; protected set; }
/// <summary>
/// Sets the value and base value, raising <see cref="AvaloniaObject.PropertyChanged"/>
/// where necessary.
/// Begins a reevaluation pass on the effective value.
/// </summary>
/// <param name="clearLocalValue">
/// Determines whether any current local value should be cleared.
/// </param>
/// <remarks>
/// This method resets the <see cref="Priority"/> and <see cref="BasePriority"/> properties
/// to Unset, pending reevaluation.
/// </remarks>
public void BeginReevaluation(bool clearLocalValue = false)
{
if (clearLocalValue || Priority != BindingPriority.LocalValue)
Priority = BindingPriority.Unset;
if (clearLocalValue || BasePriority != BindingPriority.LocalValue)
BasePriority = BindingPriority.Unset;
}
public void EndReevaluation()
{
if (Priority == BindingPriority.Unset)
{
_valueEntry?.Unsubscribe();
_valueEntry = null;
}
if (BasePriority == BindingPriority.Unset)
{
_baseValueEntry?.Unsubscribe();
_baseValueEntry = null;
}
}
/// <summary>
/// Sets the value and base value for a non-LocalValue priority, raising
/// <see cref="AvaloniaObject.PropertyChanged"/> where necessary.
/// </summary>
/// <param name="owner">The associated value store.</param>
/// <param name="property">The property being changed.</param>
/// <param name="value">The new value of the property.</param>
/// <param name="priority">The priority of the new value.</param>
public abstract void SetAndRaise(
ValueStore owner,
AvaloniaProperty property,
object? value,
IValueEntry value,
BindingPriority priority);
/// <summary>
/// Sets the value and base value, raising <see cref="AvaloniaObject.PropertyChanged"/>
/// where necessary.
/// Sets the value and base value for a non-LocalValue priority, raising
/// <see cref="AvaloniaObject.PropertyChanged"/> where necessary.
/// </summary>
/// <param name="owner">The associated value store.</param>
/// <param name="property">The property being changed.</param>
/// <param name="value">The new value of the property.</param>
/// <param name="priority">The priority of the new value.</param>
/// <param name="baseValue">The new base value of the property.</param>
/// <param name="basePriority">The priority of the new base value.</param>
public abstract void SetAndRaise(
ValueStore owner,
AvaloniaProperty property,
object? value,
IValueEntry value,
BindingPriority priority,
object? baseValue,
IValueEntry baseValue,
BindingPriority basePriority);
/// <summary>
/// Sets the value, raising <see cref="AvaloniaObject.PropertyChanged"/>
/// where necessary.
/// </summary>
/// <param name="owner">The associated value store.</param>
/// <param name="entry">The value entry with the new value of the property.</param>
/// <param name="priority">The priority of the new value.</param>
/// <remarks>
/// This method does not set the base value.
/// </remarks>
public abstract void SetAndRaise(
ValueStore owner,
IValueEntry entry,
BindingPriority priority);
/// <summary>
/// Set the value priority, but leaves the value unchanged.
/// </summary>
@ -103,6 +119,14 @@ namespace Avalonia.PropertyStore
EffectiveValue? oldValue,
EffectiveValue? newValue);
/// <summary>
/// Removes the current animation value and reverts to the base value, raising
/// <see cref="AvaloniaObject.PropertyChanged"/> where necessary.
/// </summary>
/// <param name="owner">The associated value store.</param>
/// <param name="property">The property being changed.</param>
public abstract void RemoveAnimationAndRaise(ValueStore owner, AvaloniaProperty property);
/// <summary>
/// Coerces the property value.
/// </summary>
@ -120,5 +144,28 @@ namespace Avalonia.PropertyStore
protected abstract object? GetBoxedValue();
protected abstract object? GetBoxedBaseValue();
protected void UpdateValueEntry(IValueEntry? entry, BindingPriority priority)
{
if (priority <= Priority && entry != _valueEntry)
{
_valueEntry?.Unsubscribe();
_valueEntry = entry;
}
if (priority <= BasePriority &&
priority >= BindingPriority.LocalValue &&
entry != _baseValueEntry)
{
_baseValueEntry?.Unsubscribe();
_baseValueEntry = entry;
}
}
protected void UnsubscribeValueEntries()
{
_valueEntry?.Unsubscribe();
_baseValueEntry?.Unsubscribe();
}
}
}

208
src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Data;
using JetBrains.Annotations;
namespace Avalonia.PropertyStore
{
@ -60,44 +61,117 @@ namespace Avalonia.PropertyStore
public override void SetAndRaise(
ValueStore owner,
AvaloniaProperty property,
object? value,
IValueEntry value,
BindingPriority priority)
{
// `value` should already have been converted to the correct type and
// validated by this point.
SetAndRaise(owner, (StyledPropertyBase<T>)property, (T)value!, priority);
Debug.Assert(priority != BindingPriority.LocalValue);
UpdateValueEntry(value, priority);
SetAndRaiseCore(owner, (StyledPropertyBase<T>)value.Property, (T)value.GetValue()!, priority);
}
public override void SetAndRaise(
ValueStore owner,
AvaloniaProperty property,
object? value,
IValueEntry value,
BindingPriority priority,
object? baseValue,
IValueEntry baseValue,
BindingPriority basePriority)
{
SetAndRaise(owner, (StyledPropertyBase<T>)property, (T)value!, priority, (T)baseValue!, basePriority);
Debug.Assert(priority != BindingPriority.LocalValue);
SetAndRaiseCore(owner, (StyledPropertyBase<T>)value.Property, (T)value.GetValue()!, priority, (T)baseValue!, basePriority);
}
public override void SetAndRaise(
public void SetLocalValueAndRaise(
ValueStore owner,
IValueEntry entry,
BindingPriority priority)
StyledPropertyBase<T> property,
T value)
{
var value = entry is IValueEntry<T> typed ? typed.GetValue() : (T)entry.GetValue()!;
SetAndRaise(owner, (StyledPropertyBase<T>)entry.Property, value, priority);
SetAndRaiseCore(owner, property, value, BindingPriority.LocalValue);
}
/// <summary>
/// Sets the value and base value, raising <see cref="AvaloniaObject.PropertyChanged"/>
/// where necessary.
/// </summary>
/// <param name="owner">The object on which to raise events.</param>
/// <param name="property">The property being changed.</param>
/// <param name="value">The new value of the property.</param>
/// <param name="priority">The priority of the new value.</param>
public void SetAndRaise(
public bool TryGetBaseValue([MaybeNullWhen(false)] out T value)
{
value = _baseValue!;
return BasePriority != BindingPriority.Unset;
}
public override void RaiseInheritedValueChanged(
AvaloniaObject owner,
AvaloniaProperty property,
EffectiveValue? oldValue,
EffectiveValue? newValue)
{
Debug.Assert(oldValue is not null || newValue is not null);
var p = (StyledPropertyBase<T>)property;
var o = oldValue is not null ? ((EffectiveValue<T>)oldValue).Value : p.GetDefaultValue(owner.GetType());
var n = newValue is not null ? ((EffectiveValue<T>)newValue).Value : p.GetDefaultValue(owner.GetType());
var priority = newValue is not null ? BindingPriority.Inherited : BindingPriority.Unset;
if (!EqualityComparer<T>.Default.Equals(o, n))
{
owner.RaisePropertyChanged(p, o, n, priority, true);
}
}
public override void RemoveAnimationAndRaise(ValueStore owner, AvaloniaProperty property)
{
Debug.Assert(Priority != BindingPriority.Animation);
Debug.Assert(BasePriority != BindingPriority.Unset);
UpdateValueEntry(null, BindingPriority.Animation);
SetAndRaiseCore(owner, (StyledPropertyBase<T>)property, _baseValue!, BasePriority);
}
public override void CoerceValue(ValueStore owner, AvaloniaProperty property)
{
if (_uncommon is null)
return;
SetAndRaiseCore(
owner,
(StyledPropertyBase<T>)property,
_uncommon._uncoercedValue!,
Priority,
_uncommon._uncoercedBaseValue!,
BasePriority);
}
public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property)
{
UnsubscribeValueEntries();
DisposeAndRaiseUnset(owner, (StyledPropertyBase<T>)property);
}
public void DisposeAndRaiseUnset(ValueStore owner, StyledPropertyBase<T> property)
{
BindingPriority priority;
T oldValue;
if (property.Inherits && owner.TryGetInheritedValue(property, out var i))
{
oldValue = ((EffectiveValue<T>)i).Value;
priority = BindingPriority.Inherited;
}
else
{
oldValue = property.GetDefaultValue(owner.GetType());
priority = BindingPriority.Unset;
}
if (!EqualityComparer<T>.Default.Equals(oldValue, Value))
{
owner.Owner.RaisePropertyChanged(property, Value, oldValue, priority, true);
if (property.Inherits)
owner.OnInheritedEffectiveValueDisposed(property, Value);
}
}
protected override object? GetBoxedValue() => Value;
protected override object? GetBoxedBaseValue()
{
return BasePriority != BindingPriority.Unset ? _baseValue : AvaloniaProperty.UnsetValue;
}
private void SetAndRaiseCore(
ValueStore owner,
StyledPropertyBase<T> property,
T value,
@ -144,17 +218,7 @@ namespace Avalonia.PropertyStore
}
}
/// <summary>
/// Sets the value and base value, raising <see cref="AvaloniaObject.PropertyChanged"/>
/// where necessary.
/// </summary>
/// <param name="owner">The object on which to raise events.</param>
/// <param name="property">The property being changed.</param>
/// <param name="value">The new value of the property.</param>
/// <param name="priority">The priority of the new value.</param>
/// <param name="baseValue">The new base value of the property.</param>
/// <param name="basePriority">The priority of the new base value.</param>
public void SetAndRaise(
private void SetAndRaiseCore(
ValueStore owner,
StyledPropertyBase<T> property,
T value,
@ -187,7 +251,7 @@ namespace Avalonia.PropertyStore
}
if (priority != BindingPriority.Unset &&
(BasePriority == BindingPriority.Unset ||
(BasePriority == BindingPriority.Unset ||
!EqualityComparer<T>.Default.Equals(_baseValue, bv)))
{
_baseValue = v;
@ -212,80 +276,6 @@ namespace Avalonia.PropertyStore
}
}
public bool TryGetBaseValue([MaybeNullWhen(false)] out T value)
{
value = _baseValue!;
return BasePriority != BindingPriority.Unset;
}
public override void RaiseInheritedValueChanged(
AvaloniaObject owner,
AvaloniaProperty property,
EffectiveValue? oldValue,
EffectiveValue? newValue)
{
Debug.Assert(oldValue is not null || newValue is not null);
var p = (StyledPropertyBase<T>)property;
var o = oldValue is not null ? ((EffectiveValue<T>)oldValue).Value : p.GetDefaultValue(owner.GetType());
var n = newValue is not null ? ((EffectiveValue<T>)newValue).Value : p.GetDefaultValue(owner.GetType());
var priority = newValue is not null ? BindingPriority.Inherited : BindingPriority.Unset;
if (!EqualityComparer<T>.Default.Equals(o, n))
{
owner.RaisePropertyChanged(p, o, n, priority, true);
}
}
public override void CoerceValue(ValueStore owner, AvaloniaProperty property)
{
if (_uncommon is null)
return;
SetAndRaise(
owner,
(StyledPropertyBase<T>)property,
_uncommon._uncoercedValue!,
Priority,
_uncommon._uncoercedBaseValue!,
BasePriority);
}
public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property)
{
DisposeAndRaiseUnset(owner, (StyledPropertyBase<T>)property);
}
public void DisposeAndRaiseUnset(ValueStore owner, StyledPropertyBase<T> property)
{
BindingPriority priority;
T oldValue;
if (property.Inherits && owner.TryGetInheritedValue(property, out var i))
{
oldValue = ((EffectiveValue<T>)i).Value;
priority = BindingPriority.Inherited;
}
else
{
oldValue = property.GetDefaultValue(owner.GetType());
priority = BindingPriority.Unset;
}
if (!EqualityComparer<T>.Default.Equals(oldValue, Value))
{
owner.Owner.RaisePropertyChanged(property, Value, oldValue, priority, true);
if (property.Inherits)
owner.OnInheritedEffectiveValueDisposed(property, Value);
}
}
protected override object? GetBoxedValue() => Value;
protected override object? GetBoxedBaseValue()
{
return BasePriority != BindingPriority.Unset ? _baseValue : AvaloniaProperty.UnsetValue;
}
private class UncommonFields
{
public Func<IAvaloniaObject, T, T>? _coerce;

2
src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs

@ -41,7 +41,7 @@ namespace Avalonia.PropertyStore
return e;
}
public IDisposable AddValue<T>(StyledPropertyBase<T> property, T value)
public ImmediateValueEntry<T> AddValue<T>(StyledPropertyBase<T> property, T value)
{
var e = new ImmediateValueEntry<T>(this, property, value);
Add(e);

193
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -62,10 +62,7 @@ namespace Avalonia.PropertyStore
var result = frame.AddBinding(property, source);
if (effective is null || priority <= effective.Priority)
{
result.Start();
UnsubscribeInactiveValues(frameIndex, property);
}
return result;
}
@ -92,10 +89,7 @@ namespace Avalonia.PropertyStore
var result = frame.AddBinding(property, source);
if (effective is null || priority <= effective.Priority)
{
result.Start();
UnsubscribeInactiveValues(frameIndex, property);
}
return result;
}
@ -122,10 +116,7 @@ namespace Avalonia.PropertyStore
var result = frame.AddBinding(property, source);
if (effective is null || priority <= effective.Priority)
{
result.Start();
UnsubscribeInactiveValues(frameIndex, property);
}
return result;
}
@ -177,26 +168,43 @@ namespace Avalonia.PropertyStore
throw new ArgumentException($"{value} is not a valid value for '{property.Name}.");
}
IDisposable? result = null;
if (priority != BindingPriority.LocalValue)
{
var frame = GetOrCreateImmediateValueFrame(property, priority, out var frameIndex);
result = frame.AddValue(property, value);
UnsubscribeInactiveValues(frameIndex, property);
}
var result = frame.AddValue(property, value);
if (TryGetEffectiveValue(property, out var existing))
{
var effective = (EffectiveValue<T>)existing;
effective.SetAndRaise(this, property, value, priority);
if (TryGetEffectiveValue(property, out var existing))
{
var effective = (EffectiveValue<T>)existing;
effective.SetAndRaise(this, result, priority);
}
else
{
var defaultValue = property.GetDefaultValue(Owner.GetType());
var effectiveValue = new EffectiveValue<T>(Owner, property, defaultValue, BindingPriority.Unset);
AddEffectiveValue(property, effectiveValue);
effectiveValue.SetAndRaise(this, result, priority);
}
return result;
}
else
{
AddEffectiveValueAndRaise(property, value, priority);
}
if (TryGetEffectiveValue(property, out var existing))
{
var effective = (EffectiveValue<T>)existing;
effective.SetLocalValueAndRaise(this, property, value);
}
else
{
var defaultValue = property.GetDefaultValue(Owner.GetType());
var effectiveValue = new EffectiveValue<T>(Owner, property, defaultValue, BindingPriority.Unset);
AddEffectiveValue(property, effectiveValue);
effectiveValue.SetLocalValueAndRaise(this, property, value);
}
return result;
return null;
}
}
public object? GetValue(AvaloniaProperty property)
@ -349,40 +357,15 @@ namespace Avalonia.PropertyStore
/// Called by non-LocalValue binding entries to re-evaluate the effective value when the
/// binding produces a new value.
/// </summary>
/// <param name="property">The bound property.</param>
/// <param name="entry">The binding entry.</param>
/// <param name="priority">The priority of binding which produced a new value.</param>
/// <param name="value">The new value.</param>
public void OnBindingValueChanged(
AvaloniaProperty property,
BindingPriority priority,
object? value)
IValueEntry entry,
BindingPriority priority)
{
Debug.Assert(priority != BindingPriority.LocalValue);
if (TryGetEffectiveValue(property, out var existing))
{
if (priority <= existing.Priority)
ReevaluateEffectiveValue(property, existing);
}
else
{
AddEffectiveValueAndRaise(property, value, priority);
}
}
/// <summary>
/// Called by non-LocalValue binding entries to re-evaluate the effective value when the
/// binding produces a new value.
/// </summary>
/// <param name="property">The bound property.</param>
/// <param name="priority">The priority of binding which produced a new value.</param>
/// <param name="value">The new value.</param>
public void OnBindingValueChanged<T>(
StyledPropertyBase<T> property,
BindingPriority priority,
T value)
{
Debug.Assert(priority != BindingPriority.LocalValue);
var property = entry.Property;
if (TryGetEffectiveValue(property, out var existing))
{
@ -391,7 +374,7 @@ namespace Avalonia.PropertyStore
}
else
{
AddEffectiveValueAndRaise(property, value, priority);
AddEffectiveValueAndRaise(property, entry, priority);
}
}
@ -413,8 +396,8 @@ namespace Avalonia.PropertyStore
}
/// <summary>
/// Called by a <see cref="BindingEntry{T}"/> to re-evaluate the effective value when the
/// binding completes or terminates on error.
/// Called by a <see cref="BindingEntryBase{TValue, TSource}"/> to re-evaluate the
/// effective value when the binding completes or terminates on error.
/// </summary>
/// <param name="property">The previously bound property.</param>
/// <param name="frame">The frame which contained the binding.</param>
@ -697,31 +680,14 @@ namespace Avalonia.PropertyStore
/// event and notifies inheritance children if necessary .
/// </summary>
/// <param name="property">The property.</param>
/// <param name="value">The property value.</param>
/// <param name="entry">The value entry.</param>
/// <param name="priority">The value priority.</param>
private void AddEffectiveValueAndRaise(AvaloniaProperty property, object? value, BindingPriority priority)
private void AddEffectiveValueAndRaise(AvaloniaProperty property, IValueEntry entry, BindingPriority priority)
{
Debug.Assert(priority < BindingPriority.Inherited);
var effectiveValue = property.CreateEffectiveValue(Owner);
AddEffectiveValue(property, effectiveValue);
effectiveValue.SetAndRaise(this, property, value, priority);
}
/// <summary>
/// Adds a new effective value, raises the initial <see cref="AvaloniaObject.PropertyChanged"/>
/// event and notifies inheritance children if necessary .
/// </summary>
/// <typeparam name="T">The property type.</typeparam>
/// <param name="property">The property.</param>
/// <param name="value">The property value.</param>
/// <param name="priority">The value priority.</param>
private void AddEffectiveValueAndRaise<T>(StyledPropertyBase<T> property, T value, BindingPriority priority)
{
Debug.Assert(priority < BindingPriority.Inherited);
var defaultValue = property.GetDefaultValue(Owner.GetType());
var effectiveValue = new EffectiveValue<T>(Owner, property, defaultValue, BindingPriority.Unset);
AddEffectiveValue(property, effectiveValue);
effectiveValue.SetAndRaise(this, property, value, priority);
effectiveValue.SetAndRaise(this, entry, priority);
}
private bool RemoveEffectiveValue(AvaloniaProperty property)
@ -796,14 +762,8 @@ namespace Avalonia.PropertyStore
var generation = _frameGeneration;
// Reset all non-LocalValue effective value to Unset priority.
if (current is not null)
{
if (ignoreLocalValue || current.Priority != BindingPriority.LocalValue)
current.SetPriority(BindingPriority.Unset);
if (ignoreLocalValue || current.BasePriority != BindingPriority.LocalValue)
current.SetBasePriority(BindingPriority.Unset);
}
// Notify the existing effective value that reevaluation is starting.
current?.BeginReevaluation(ignoreLocalValue);
// Iterate the frames to get the effective value.
for (var i = _frames.Count - 1; i >= 0; --i)
@ -821,7 +781,16 @@ namespace Avalonia.PropertyStore
return;
}
if (foundEntry && entry!.HasValue)
// 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 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 > BindingPriority.Animation && priority < current.BasePriority);
if (foundEntry && isRelevantPriority && entry!.HasValue)
{
if (current is not null)
{
@ -838,19 +807,13 @@ namespace Avalonia.PropertyStore
if (generation != _frameGeneration)
goto restart;
if (current is not null &&
current.Priority < BindingPriority.Unset &&
current.BasePriority < BindingPriority.Unset)
{
// If the active state of one of the trailing frames has changed since
// the last read, then we need to re-evaluate the effective values of all
// properties.
if (UnsubscribeInactiveValues(i, property))
ReevaluateEffectiveValues();
return;
}
if (current?.Priority < BindingPriority.Unset &&
current?.BasePriority < BindingPriority.Unset)
break;
}
current?.EndReevaluation();
if (current?.Priority == BindingPriority.Unset)
{
if (current.BasePriority == BindingPriority.Unset)
@ -860,7 +823,7 @@ namespace Avalonia.PropertyStore
}
else
{
current.SetAndRaise(this, property, current.BaseValue, current.BasePriority);
current.RemoveAnimationAndRaise(this, property);
}
}
}
@ -885,16 +848,9 @@ namespace Avalonia.PropertyStore
var generation = _frameGeneration;
var count = _effectiveValues.Count;
// Reset all non-LocalValue effective values to Unset priority.
// Notify the existing effective values that reevaluation is starting.
for (var i = 0; i < count; ++i)
{
var e = _effectiveValues[i];
if (e.Priority != BindingPriority.LocalValue)
e.SetPriority(BindingPriority.Unset);
if (e.BasePriority != BindingPriority.LocalValue)
e.SetBasePriority(BindingPriority.Unset);
}
_effectiveValues[i].BeginReevaluation();
// Iterate the frames, setting and creating effective values.
for (var i = _frames.Count - 1; i >= 0; --i)
@ -912,10 +868,9 @@ namespace Avalonia.PropertyStore
{
var entry = frame.GetEntry(j);
var property = entry.Property;
EffectiveValue? effectiveValue;
// Unsubscribe and skip if we already have a value/base value for this property.
if (_effectiveValues.TryGetValue(property, out effectiveValue) == true &&
if (_effectiveValues.TryGetValue(property, out var effectiveValue) == true &&
effectiveValue.BasePriority < BindingPriority.Unset)
{
entry.Unsubscribe();
@ -949,6 +904,7 @@ namespace Avalonia.PropertyStore
for (var i = 0; i < count; ++i)
{
_effectiveValues.GetKeyValue(i, out var key, out var e);
e.EndReevaluation();
if (e.Priority == BindingPriority.Unset)
{
@ -973,33 +929,6 @@ namespace Avalonia.PropertyStore
}
}
private bool UnsubscribeInactiveValues(int activeFrameIndex, AvaloniaProperty property)
{
var foundBaseValue = _frames[activeFrameIndex].Priority != BindingPriority.Animation;
var activeChanged = false;
for (var i = activeFrameIndex - 1; i >= 0; --i)
{
var frame = _frames[i];
if (!foundBaseValue && frame.Priority > BindingPriority.Animation)
{
foundBaseValue = true;
continue;
}
if ((foundBaseValue || frame.Priority <= BindingPriority.Animation) &&
frame.TryGetEntryIfActive(property, out var entry, out var changed))
{
if (changed && frame.EntryCount > 1)
activeChanged = true;
entry.Unsubscribe();
}
}
return activeChanged;
}
private bool TryGetEffectiveValue(
AvaloniaProperty property,
[NotNullWhen(true)] out EffectiveValue? value)

Loading…
Cancel
Save