Browse Source

Merge pull request #8373 from AvaloniaUI/fixes/8372-clear-local-value

Correctly clear PriorityValue local value
Max Katz 3 years ago
parent
commit
138791e3e6

+ 1 - 0
src/Avalonia.Base/PropertyStore/PriorityValue.cs

@@ -121,6 +121,7 @@ namespace Avalonia.PropertyStore
 
         public void ClearLocalValue()
         {
+            _localValue = default;
             UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs<T>(
                 _owner,
                 Property,

+ 20 - 7
src/Avalonia.Base/Styling/PropertySetterInstance.cs

@@ -18,7 +18,7 @@ namespace Avalonia.Styling
         private readonly DirectPropertyBase<T>? _directProperty;
         private readonly T _value;
         private IDisposable? _subscription;
-        private bool _isActive;
+        private State _state;
 
         public PropertySetterInstance(
             IStyleable target,
@@ -40,6 +40,8 @@ namespace Avalonia.Styling
             _value = value;
         }
 
+        private bool IsActive => _state == State.Active;
+
         public void Start(bool hasActivator)
         {
             if (hasActivator)
@@ -70,31 +72,35 @@ namespace Avalonia.Styling
 
         public void Activate()
         {
-            if (!_isActive)
+            if (!IsActive)
             {
-                _isActive = true;
+                _state = State.Active;
                 PublishNext();
             }
         }
 
         public void Deactivate()
         {
-            if (_isActive)
+            if (IsActive)
             {
-                _isActive = false;
+                _state = State.Inactive;
                 PublishNext();
             }
         }
 
         public override void Dispose()
         {
+            if (_state == State.Disposed)
+                return;
+            _state = State.Disposed;
+
             if (_subscription is object)
             {
                 var sub = _subscription;
                 _subscription = null;
                 sub.Dispose();
             }
-            else if (_isActive)
+            else if (IsActive)
             {
                 if (_styledProperty is object)
                 {
@@ -114,7 +120,14 @@ namespace Avalonia.Styling
 
         private void PublishNext()
         {
-            PublishNext(_isActive ? new BindingValue<T>(_value) : default);
+            PublishNext(IsActive ? new BindingValue<T>(_value) : default);
+        }
+
+        private enum State
+        {
+            Inactive,
+            Active,
+            Disposed,
         }
     }
 }

+ 15 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs

@@ -17,6 +17,21 @@ namespace Avalonia.Base.UnitTests
             Assert.Equal("foodefault", target.GetValue(Class1.FooProperty));
         }
 
+        [Fact]
+        public void ClearValue_Resets_Value_To_Style_value()
+        {
+            Class1 target = new Class1();
+
+            target.SetValue(Class1.FooProperty, "style", BindingPriority.Style);
+            target.SetValue(Class1.FooProperty, "local");
+
+            Assert.Equal("local", target.GetValue(Class1.FooProperty));
+
+            target.ClearValue(Class1.FooProperty);
+
+            Assert.Equal("style", target.GetValue(Class1.FooProperty));
+        }
+
         [Fact]
         public void ClearValue_Raises_PropertyChanged()
         {

+ 36 - 6
tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs

@@ -150,13 +150,43 @@ namespace Avalonia.Base.UnitTests.Styling
             Assert.Equal(BindingPriority.StyleTrigger, control.GetDiagnostic(TextBlock.TagProperty).Priority);
         }
 
-        private IBinding CreateMockBinding(AvaloniaProperty property)
+        [Fact]
+        public void Disposing_Setter_Should_Preserve_LocalValue()
         {
-            var subject = new Subject<object>();
-            var descriptor = InstancedBinding.OneWay(subject);
-            var binding = Mock.Of<IBinding>(x => 
-                x.Initiate(It.IsAny<IAvaloniaObject>(), property, null, false) == descriptor);
-            return binding;
+            var control = new Canvas();
+            var setter = new Setter(TextBlock.TagProperty, "foo");
+
+            var instance = setter.Instance(control);
+            instance.Start(true);
+            instance.Activate();
+
+            control.Tag = "bar";
+
+            instance.Dispose();
+
+            Assert.Equal("bar", control.Tag);
+        }
+
+        [Fact]
+        public void Disposing_Binding_Setter_Should_Preserve_LocalValue()
+        {
+            var control = new Canvas();
+            var source = new { Foo = "foo" };
+            var setter = new Setter(TextBlock.TagProperty, new Binding
+            {
+                Source = source,
+                Path = nameof(source.Foo),
+            });
+
+            var instance = setter.Instance(control);
+            instance.Start(true);
+            instance.Activate();
+
+            control.Tag = "bar";
+
+            instance.Dispose();
+
+            Assert.Equal("bar", control.Tag);
         }
 
         private class TestConverter : IValueConverter