Browse Source

Correctly unsubscribe animation value entries.

pull/8600/head
Steven Kirk 4 years ago
parent
commit
9b2d9be1fa
  1. 44
      src/Avalonia.Base/PropertyStore/EffectiveValue.cs
  2. 2
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  3. 15
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs
  4. 20
      tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_OnPropertyChanged.cs

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

@ -1,4 +1,5 @@
using Avalonia.Data; using System.Diagnostics;
using Avalonia.Data;
namespace Avalonia.PropertyStore namespace Avalonia.PropertyStore
{ {
@ -147,18 +148,41 @@ namespace Avalonia.PropertyStore
protected void UpdateValueEntry(IValueEntry? entry, BindingPriority priority) protected void UpdateValueEntry(IValueEntry? entry, BindingPriority priority)
{ {
if (priority <= Priority && entry != _valueEntry) Debug.Assert(priority != BindingPriority.LocalValue);
if (priority <= BindingPriority.Animation)
{ {
_valueEntry?.Unsubscribe(); // If we've received an animation value and the current value is a non-animation
_valueEntry = entry; // value, then the current entry becomes our base entry.
if (Priority > BindingPriority.LocalValue && Priority < BindingPriority.Inherited)
{
Debug.Assert(_valueEntry is not null);
_baseValueEntry = _valueEntry;
_valueEntry = null;
}
if (_valueEntry != entry)
{
_valueEntry?.Unsubscribe();
_valueEntry = entry;
}
} }
else if (Priority <= BindingPriority.Animation)
if (priority <= BasePriority &&
priority >= BindingPriority.LocalValue &&
entry != _baseValueEntry)
{ {
_baseValueEntry?.Unsubscribe(); // We've received a non-animation value and have an active animation value, so the
_baseValueEntry = entry; // new entry becomes our base entry.
if (_baseValueEntry != entry)
{
_baseValueEntry?.Unsubscribe();
_baseValueEntry = entry;
}
}
else if (_valueEntry != entry)
{
// Both the current value and the new value are non-animation values, so the new
// entry replaces the existing entry.
_valueEntry?.Unsubscribe();
_valueEntry = entry;
} }
} }

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

@ -369,7 +369,7 @@ namespace Avalonia.PropertyStore
if (TryGetEffectiveValue(property, out var existing)) if (TryGetEffectiveValue(property, out var existing))
{ {
if (priority <= existing.Priority) if (priority <= existing.BasePriority)
ReevaluateEffectiveValue(property, existing); ReevaluateEffectiveValue(property, existing);
} }
else else

15
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@ -562,6 +562,21 @@ namespace Avalonia.Base.UnitTests
Assert.Equal(0, source.SubscriberCount); Assert.Equal(0, source.SubscriberCount);
} }
[Theory]
[InlineData(BindingPriority.LocalValue)]
[InlineData(BindingPriority.Style)]
public void Observable_Is_Not_Unsubscribed_When_Animation_Value_Is_Set(BindingPriority priority)
{
var source = new TestSubject<BindingValue<string>>("foo");
var target = new Class1();
target.Bind(Class1.FooProperty, source, priority);
Assert.Equal(1, source.SubscriberCount);
target.SetValue(Class1.FooProperty, "bar", BindingPriority.Animation);
Assert.Equal(1, source.SubscriberCount);
}
[Theory] [Theory]
[InlineData(BindingPriority.LocalValue)] [InlineData(BindingPriority.LocalValue)]
[InlineData(BindingPriority.Style)] [InlineData(BindingPriority.Style)]

20
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_OnPropertyChanged.cs

@ -48,6 +48,26 @@ namespace Avalonia.Base.UnitTests
Assert.False(change.IsEffectiveValueChange); Assert.False(change.IsEffectiveValueChange);
} }
[Fact]
public void OnPropertyChangedCore_Is_Called_On_Non_Effective_Property_Binding_Value_Change()
{
var target = new Class1();
var source = new BehaviorSubject<BindingValue<string>>("styled1");
target.Bind(Class1.FooProperty, source, BindingPriority.Style);
target.SetValue(Class1.FooProperty, "newvalue", BindingPriority.Animation);
source.OnNext("styled2");
Assert.Equal(3, target.CoreChanges.Count);
var change = (AvaloniaPropertyChangedEventArgs<string>)target.CoreChanges[2];
Assert.Equal("styled2", change.NewValue.Value);
Assert.False(change.OldValue.HasValue);
Assert.Equal(BindingPriority.Style, change.Priority);
Assert.False(change.IsEffectiveValueChange);
}
[Fact] [Fact]
public void OnPropertyChanged_Is_Called_Only_For_Effective_Value_Changes() public void OnPropertyChanged_Is_Called_Only_For_Effective_Value_Changes()
{ {

Loading…
Cancel
Save