From dd1709732324739db36ce30fbc7e8d45c091b172 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 15 Feb 2023 10:05:17 +0100 Subject: [PATCH 1/4] Added failing test for #10255 . --- .../AvaloniaObjectTests_Binding.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index baaed5104d..b6036bba8f 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -167,6 +167,22 @@ namespace Avalonia.Base.UnitTests Assert.Equal("foo", target.GetValue(property)); } + [Fact] + public void Completing_Animation_Binding_Reverts_To_Set_LocalValue_With_Style_Value() + { + var target = new Class1(); + var source = new Subject>(); + var property = Class1.FooProperty; + + target.SetValue(property, "style", BindingPriority.Style); + target.SetValue(property, "foo"); + target.Bind(property, source, BindingPriority.Animation); + source.OnNext("bar"); + source.OnCompleted(); + + Assert.Equal("foo", target.GetValue(property)); + } + [Fact] public void Completing_LocalValue_Binding_Raises_PropertyChanged() { From 3e81ed8fbca08a36eeec53db5792a31440283d2f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 15 Feb 2023 11:04:03 +0100 Subject: [PATCH 2/4] Revert to LocalValue when animation completes. Fixes #10255 . --- src/Avalonia.Base/PropertyStore/ValueStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index 8b702665f8..7734369ff0 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -799,7 +799,7 @@ namespace Avalonia.PropertyStore // - 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 < current.Priority && priority < current.BasePriority) || (priority > BindingPriority.Animation && priority < current.BasePriority); if (foundEntry && isRelevantPriority && entry!.HasValue) From 2cff53c05713f6d52d4a3120a0cec77ea238911d Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 15 Feb 2023 11:05:34 +0100 Subject: [PATCH 3/4] Added failing test for #10345. --- .../ValueStoreTests_Inheritance.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs index ed122e4ddc..91b0f9cdee 100644 --- a/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs +++ b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs @@ -97,6 +97,27 @@ namespace Avalonia.Base.UnitTests.PropertyStore Assert.Same(parentStore, grandchild.GetValueStore().InheritanceAncestor); } + [Fact] + public void Child_Notifies_About_Setting_Back_To_Default_Value() + { + var parent = new Class1(); + var child = new Class1(); + + parent.Foo = "changed"; + child.Parent = parent; + + bool raised = false; + child.PropertyChanged += (_, args) => + { + raised = args.Property == Class1.FooProperty && args.GetNewValue() == "foodefault"; + }; + + Assert.Equal("changed", child.Foo); // inherited from parent. + + child.Foo = "foodefault"; // reset back to default. + Assert.True(raised); // expect event to be raised, as actual value was changed. + } + [Fact] public void Adding_Child_Sets_InheritanceAncestor() { From 994897a0238caf1882060f41e0e4634efda4a34a Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 15 Feb 2023 11:32:56 +0100 Subject: [PATCH 4/4] Initialize EffectiveValue with inherited value. Fixes #10345 --- .../PropertyStore/EffectiveValue`1.cs | 7 +++++-- src/Avalonia.Base/PropertyStore/ValueStore.cs | 16 +++++++++++++--- src/Avalonia.Base/StyledProperty.cs | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs index 0d93e9d8ed..c469034f9b 100644 --- a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs +++ b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs @@ -19,13 +19,16 @@ namespace Avalonia.PropertyStore private T? _baseValue; private UncommonFields? _uncommon; - public EffectiveValue(AvaloniaObject owner, StyledProperty property) + public EffectiveValue( + AvaloniaObject owner, + StyledProperty property, + EffectiveValue? inherited) { Priority = BindingPriority.Unset; BasePriority = BindingPriority.Unset; _metadata = property.GetMetadata(owner.GetType()); - var value = _metadata.DefaultValue; + var value = inherited is null ? _metadata.DefaultValue : inherited.Value; if (property.HasCoercion && _metadata.CoerceValue is { } coerce) { diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs index 7734369ff0..ec6ed392c1 100644 --- a/src/Avalonia.Base/PropertyStore/ValueStore.cs +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -184,7 +184,7 @@ namespace Avalonia.PropertyStore } else { - var effectiveValue = new EffectiveValue(Owner, property); + var effectiveValue = CreateEffectiveValue(property); AddEffectiveValue(property, effectiveValue); effectiveValue.SetAndRaise(this, result, priority); } @@ -200,7 +200,7 @@ namespace Avalonia.PropertyStore } else { - var effectiveValue = new EffectiveValue(Owner, property); + var effectiveValue = CreateEffectiveValue(property); AddEffectiveValue(property, effectiveValue); effectiveValue.SetLocalValueAndRaise(this, property, value); } @@ -217,7 +217,7 @@ namespace Avalonia.PropertyStore } else { - var effectiveValue = new EffectiveValue(Owner, property); + var effectiveValue = CreateEffectiveValue(property); AddEffectiveValue(property, effectiveValue); effectiveValue.SetCurrentValueAndRaise(this, property, value); } @@ -287,6 +287,16 @@ namespace Avalonia.PropertyStore return false; } + public EffectiveValue CreateEffectiveValue(StyledProperty property) + { + EffectiveValue? inherited = null; + + if (property.Inherits && TryGetInheritedValue(property, out var v)) + inherited = (EffectiveValue)v; + + return new EffectiveValue(Owner, property, inherited); + } + public void SetInheritanceParent(AvaloniaObject? newParent) { var values = AvaloniaPropertyDictionaryPool.Get(); diff --git a/src/Avalonia.Base/StyledProperty.cs b/src/Avalonia.Base/StyledProperty.cs index ad1f09066e..8695918c18 100644 --- a/src/Avalonia.Base/StyledProperty.cs +++ b/src/Avalonia.Base/StyledProperty.cs @@ -171,7 +171,7 @@ namespace Avalonia internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o) { - return new EffectiveValue(o, this); + return o.GetValueStore().CreateEffectiveValue(this); } ///