From d5dc4704164c6fddd5465e827fcdacbb310c4703 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 21 Nov 2019 18:58:14 +0100 Subject: [PATCH] Fixed issue with LocalValue and bindings. --- .../PropertyStore/LocalValueEntry.cs | 12 ++-- .../PropertyStore/PriorityValue.cs | 56 +++++++++++++------ src/Avalonia.Base/ValueStore.cs | 9 +-- .../AvaloniaObjectTests_Binding.cs | 31 ++++++++++ 4 files changed, 79 insertions(+), 29 deletions(-) diff --git a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs index 416185df057..067ed7b9663 100644 --- a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs +++ b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs @@ -6,14 +6,12 @@ namespace Avalonia.PropertyStore { internal class LocalValueEntry : IValue { - public LocalValueEntry(T value) => Value = value; - public Optional Value { get; set; } + private T _value; + + public LocalValueEntry(T value) => _value = value; + public Optional Value => _value; public BindingPriority ValuePriority => BindingPriority.LocalValue; Optional IValue.Value => Value.ToObject(); - - public ConstantValueEntry ToConstantValueEntry(StyledPropertyBase property) - { - return new ConstantValueEntry(property, Value.Value, BindingPriority.LocalValue); - } + public void SetValue(T value) => _value = value; } } diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs index e33ab48353c..1a4b616bd9a 100644 --- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs +++ b/src/Avalonia.Base/PropertyStore/PriorityValue.cs @@ -40,6 +40,18 @@ public PriorityValue( } } + public PriorityValue( + IAvaloniaObject owner, + StyledPropertyBase property, + IValueSink sink, + LocalValueEntry existing) + : this(owner, property, sink) + { + _localValue = existing.Value; + Value = _localValue; + ValuePriority = BindingPriority.LocalValue; + } + public StyledPropertyBase Property { get; } public Optional Value { get; private set; } public BindingPriority ValuePriority { get; private set; } @@ -77,7 +89,11 @@ void IValueSink.ValueChanged( Optional oldValue, BindingValue newValue) { - _localValue = default; + if (priority == BindingPriority.LocalValue) + { + _localValue = default; + } + UpdateEffectiveValue(); } @@ -108,29 +124,37 @@ private void UpdateEffectiveValue() var reachedLocalValues = false; var value = default(Optional); - for (var i = _entries.Count - 1; i >= 0; --i) + if (_entries.Count > 0) { - var entry = _entries[i]; - - if (!reachedLocalValues && entry.Priority >= BindingPriority.LocalValue) + for (var i = _entries.Count - 1; i >= 0; --i) { - reachedLocalValues = true; + var entry = _entries[i]; - if (_localValue.HasValue) + if (!reachedLocalValues && entry.Priority >= BindingPriority.LocalValue) { - value = _localValue; - ValuePriority = BindingPriority.LocalValue; - break; + reachedLocalValues = true; + + if (_localValue.HasValue) + { + value = _localValue; + ValuePriority = BindingPriority.LocalValue; + break; + } } - } - if (entry.Value.HasValue) - { - value = entry.Value; - ValuePriority = entry.Priority; - break; + if (entry.Value.HasValue) + { + value = entry.Value; + ValuePriority = entry.Priority; + break; + } } } + else if (_localValue.HasValue) + { + value = _localValue; + ValuePriority = BindingPriority.LocalValue; + } if (value != Value) { diff --git a/src/Avalonia.Base/ValueStore.cs b/src/Avalonia.Base/ValueStore.cs index ddcf7ee9c19..b63692a03b2 100644 --- a/src/Avalonia.Base/ValueStore.cs +++ b/src/Avalonia.Base/ValueStore.cs @@ -170,14 +170,12 @@ private void SetExisting( if (priority == BindingPriority.LocalValue) { var old = l.Value; - l.Value = value; + l.SetValue(value); _sink.ValueChanged(property, priority, old, value); } else { - var existing = l.ToConstantValueEntry(property); - var priorityValue = new PriorityValue(_owner, property, this, existing); - priorityValue.SetValue(value, priority); + var priorityValue = new PriorityValue(_owner, property, this, l); _values.SetValue(property, priorityValue); } } @@ -205,8 +203,7 @@ private IDisposable BindExisting( } else if (slot is LocalValueEntry l) { - var existing = l.ToConstantValueEntry(property); - priorityValue = new PriorityValue(_owner, property, this, existing); + priorityValue = new PriorityValue(_owner, property, this, l); } else { diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index 38f47ab95a6..4c00d2a1ea6 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -101,6 +101,37 @@ public void Completing_LocalValue_Binding_Reverts_To_Default_Value_Even_When_Loc Assert.Equal("foodefault", target.GetValue(property)); } + [Fact] + public void Completing_LocalValue_Binding_Should_Not_Revert_To_Set_LocalValue() + { + var target = new Class1(); + var source = new BehaviorSubject("bar"); + + target.SetValue(Class1.FooProperty, "foo"); + var sub = target.Bind(Class1.FooProperty, source); + + Assert.Equal("bar", target.GetValue(Class1.FooProperty)); + + sub.Dispose(); + + Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Completing_Animation_Binding_Reverts_To_Set_LocalValue() + { + var target = new Class1(); + var source = new Subject(); + var property = Class1.FooProperty; + + target.SetValue(property, "foo"); + target.Bind(property, source, BindingPriority.Animation); + source.OnNext("bar"); + source.OnCompleted(); + + Assert.Equal("foo", target.GetValue(property)); + } + [Fact] public void Setting_Style_Value_Overrides_Binding_Permanently() {