Browse Source

Fixed issue with LocalValue and bindings.

Steven Kirk 6 years ago
parent
commit
d5dc470416

+ 5 - 7
src/Avalonia.Base/PropertyStore/LocalValueEntry.cs

@@ -6,14 +6,12 @@ namespace Avalonia.PropertyStore
 {
     internal class LocalValueEntry<T> : IValue<T>
     {
-        public LocalValueEntry(T value) => Value = value;
-        public Optional<T> Value { get; set; }
+        private T _value;
+
+        public LocalValueEntry(T value) => _value = value;
+        public Optional<T> Value => _value;
         public BindingPriority ValuePriority => BindingPriority.LocalValue;
         Optional<object> IValue.Value => Value.ToObject();
-        
-        public ConstantValueEntry<T> ToConstantValueEntry(StyledPropertyBase<T> property)
-        {
-            return new ConstantValueEntry<T>(property, Value.Value, BindingPriority.LocalValue);
-        }
+        public void SetValue(T value) => _value = value;
     }
 }

+ 40 - 16
src/Avalonia.Base/PropertyStore/PriorityValue.cs

@@ -40,6 +40,18 @@ namespace Avalonia.PropertyStore
             }
         }
 
+        public PriorityValue(
+            IAvaloniaObject owner,
+            StyledPropertyBase<T> property,
+            IValueSink sink,
+            LocalValueEntry<T> existing)
+            : this(owner, property, sink)
+        {
+            _localValue = existing.Value;
+            Value = _localValue;
+            ValuePriority = BindingPriority.LocalValue;
+        }
+
         public StyledPropertyBase<T> Property { get; }
         public Optional<T> Value { get; private set; }
         public BindingPriority ValuePriority { get; private set; }
@@ -77,7 +89,11 @@ namespace Avalonia.PropertyStore
             Optional<TValue> oldValue,
             BindingValue<TValue> newValue)
         {
-            _localValue = default;
+            if (priority == BindingPriority.LocalValue)
+            {
+                _localValue = default;
+            }
+
             UpdateEffectiveValue();
         }
 
@@ -108,29 +124,37 @@ namespace Avalonia.PropertyStore
             var reachedLocalValues = false;
             var value = default(Optional<T>);
 
-            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)
             {

+ 3 - 6
src/Avalonia.Base/ValueStore.cs

@@ -170,14 +170,12 @@ namespace Avalonia
                 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<T>(_owner, property, this, existing);
-                    priorityValue.SetValue(value, priority);
+                    var priorityValue = new PriorityValue<T>(_owner, property, this, l);
                     _values.SetValue(property, priorityValue);
                 }
             }
@@ -205,8 +203,7 @@ namespace Avalonia
             }
             else if (slot is LocalValueEntry<T> l)
             {
-                var existing = l.ToConstantValueEntry(property);
-                priorityValue = new PriorityValue<T>(_owner, property, this, existing);
+                priorityValue = new PriorityValue<T>(_owner, property, this, l);
             }
             else
             {

+ 31 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs

@@ -101,6 +101,37 @@ namespace Avalonia.Base.UnitTests
             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<string>("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<string>();
+            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()
         {