浏览代码

Refactored inheritance.

To remove `InheritanceFrame` - it was unneeded, we can just point to the nearest ancestor value store with inherited values.
Steven Kirk 3 年之前
父节点
当前提交
483bbd2e26

+ 0 - 34
src/Avalonia.Base/PropertyStore/InheritanceFrame.cs

@@ -1,34 +0,0 @@
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Avalonia.PropertyStore
-{
-    internal class InheritanceFrame : Dictionary<AvaloniaProperty, EffectiveValue>
-    {
-        public InheritanceFrame(ValueStore owner, InheritanceFrame? parent = null)
-        {
-            Owner = owner;
-            Parent = parent;
-        }
-
-        public ValueStore Owner { get; }
-        public InheritanceFrame? Parent { get; private set; }
-
-        public bool TryGetFromThisOrAncestor(AvaloniaProperty property, [NotNullWhen(true)] out EffectiveValue? value)
-        {
-            var frame = this;
-
-            while (frame is object)
-            {
-                if (frame.TryGetValue(property, out value))
-                    return true;
-                frame = frame.Parent;
-            }
-
-            value = default;
-            return false;
-        }
-
-        public void SetParent(InheritanceFrame? value) => Parent = value;
-    }
-}

+ 139 - 127
src/Avalonia.Base/PropertyStore/ValueStore.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
 using Avalonia.Collections.Pooled;
 using Avalonia.Collections.Pooled;
 using Avalonia.Data;
 using Avalonia.Data;
 using Avalonia.Diagnostics;
 using Avalonia.Diagnostics;
@@ -14,14 +13,15 @@ namespace Avalonia.PropertyStore
     {
     {
         private readonly List<IValueFrame> _frames = new();
         private readonly List<IValueFrame> _frames = new();
         private Dictionary<int, IDisposable>? _localValueBindings;
         private Dictionary<int, IDisposable>? _localValueBindings;
-        private InheritanceFrame? _inheritanceFrame;
         private Dictionary<AvaloniaProperty, EffectiveValue>? _effectiveValues;
         private Dictionary<AvaloniaProperty, EffectiveValue>? _effectiveValues;
+        private int _inheritedValueCount;
         private int _frameGeneration;
         private int _frameGeneration;
         private int _styling;
         private int _styling;
 
 
         public ValueStore(AvaloniaObject owner) => Owner = owner;
         public ValueStore(AvaloniaObject owner) => Owner = owner;
 
 
         public AvaloniaObject Owner { get; }
         public AvaloniaObject Owner { get; }
+        public ValueStore? InheritanceAncestor { get; private set; }
         public IReadOnlyList<IValueFrame> Frames => _frames;
         public IReadOnlyList<IValueFrame> Frames => _frames;
 
 
         public void BeginStyling() => ++_styling;
         public void BeginStyling() => ++_styling;
@@ -160,7 +160,7 @@ namespace Avalonia.PropertyStore
         {
         {
             if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
             if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
                 return v.Value;
                 return v.Value;
-            if (_inheritanceFrame is not null && _inheritanceFrame.TryGetFromThisOrAncestor(property, out v))
+            if (property.Inherits && TryGetInheritedValue(property, out v))
                 return v.Value;
                 return v.Value;
 
 
             return GetDefaultValue(property);
             return GetDefaultValue(property);
@@ -170,7 +170,7 @@ namespace Avalonia.PropertyStore
         {
         {
             if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
             if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var v))
                 return ((EffectiveValue<T>)v).Value;
                 return ((EffectiveValue<T>)v).Value;
-            if (_inheritanceFrame is not null && _inheritanceFrame.TryGetFromThisOrAncestor(property, out v))
+            if (property.Inherits && TryGetInheritedValue(property, out v))
                 return ((EffectiveValue<T>)v).Value;
                 return ((EffectiveValue<T>)v).Value;
             return property.GetDefaultValue(Owner.GetType());
             return property.GetDefaultValue(Owner.GetType());
         }
         }
@@ -203,41 +203,57 @@ namespace Avalonia.PropertyStore
         public void SetInheritanceParent(AvaloniaObject? oldParent, AvaloniaObject? newParent)
         public void SetInheritanceParent(AvaloniaObject? oldParent, AvaloniaObject? newParent)
         {
         {
             var values = DictionaryPool<AvaloniaProperty, OldNewValue>.Get();
             var values = DictionaryPool<AvaloniaProperty, OldNewValue>.Get();
-            var oldInheritanceFrame = oldParent?.GetValueStore()._inheritanceFrame;
-            var newInheritanceFrame = newParent?.GetValueStore().OnBecameInheritanceParent();
+            var oldAncestor = InheritanceAncestor;
+            var newAncestor = newParent?.GetValueStore();
 
 
-            // The old and new parents are the same, nothing to do here.
-            if (oldInheritanceFrame == newInheritanceFrame)
+            if (newAncestor?._inheritedValueCount == 0)
+                newAncestor = newAncestor.InheritanceAncestor;
+
+            // The old and new inheritance ancestors are the same, nothing to do here.
+            if (oldAncestor == newAncestor)
                 return;
                 return;
 
 
-            // First get the old values from the old inheritance parent.
-            var f = oldInheritanceFrame;
+            // First get the old values from the old inheritance ancestor.
+            var f = oldAncestor;
 
 
             while (f is not null)
             while (f is not null)
             {
             {
-                foreach (var i in f)
+                Debug.Assert(f._effectiveValues is not null);
+
+                if (f._effectiveValues is not null)
                 {
                 {
-                    values.TryAdd(i.Key, new(i.Value));
+                    foreach (var i in f._effectiveValues)
+                    {
+                        if (i.Key.Inherits)
+                            values.TryAdd(i.Key, new(i.Value));
+                    }
                 }
                 }
-                f = f.Parent;
+
+                f = f.InheritanceAncestor;
             }
             }
 
 
-            f = newInheritanceFrame;
+            f = newAncestor;
 
 
-            // Get the new values from the new inheritance parent.
+            // Get the new values from the new inheritance ancestor.
             while (f is not null)
             while (f is not null)
             {
             {
-                foreach (var i in f)
+                Debug.Assert(f._effectiveValues is not null);
+
+                foreach (var i in f._effectiveValues)
                 {
                 {
-                    if (values.TryGetValue(i.Key, out var existing))
-                        values[i.Key] = existing.WithNewValue(i.Value);
-                    else
-                        values.Add(i.Key, new(null, i.Value));
+                    if (i.Key.Inherits)
+                    {
+                        if (values.TryGetValue(i.Key, out var existing))
+                            values[i.Key] = existing.WithNewValue(i.Value);
+                        else
+                            values.Add(i.Key, new(null, i.Value));
+                    }
                 }
                 }
-                f = f.Parent;
+
+                f = f.InheritanceAncestor;
             }
             }
 
 
-            ParentInheritanceFrameChanged(newInheritanceFrame);
+            OnInheritanceAncestorChanged(newAncestor);
 
 
             // Raise PropertyChanged events where necessary on this object and inheritance children.
             // Raise PropertyChanged events where necessary on this object and inheritance children.
             foreach (var i in values)
             foreach (var i in values)
@@ -257,27 +273,6 @@ namespace Avalonia.PropertyStore
             ReevaluateEffectiveValues();
             ReevaluateEffectiveValues();
         }
         }
 
 
-        /// <summary>
-        /// Called by an inheritance child to notify the value store that it has become an
-        /// inheritance parent. Creates and returns an inheritance frame if necessary.
-        /// </summary>
-        /// <returns></returns>
-        public InheritanceFrame? OnBecameInheritanceParent()
-        {
-            if (_inheritanceFrame is not null)
-                return _inheritanceFrame;
-            if (_effectiveValues is null)
-                return null;
-
-            foreach (var i in _effectiveValues)
-            {
-                if (i.Key.Inherits)
-                    return GetOrCreateInheritanceFrame(true);
-            }
-
-            return null;
-        }
-
         /// <summary>
         /// <summary>
         /// Called by non-LocalValue binding entries to re-evaluate the effective value when the
         /// Called by non-LocalValue binding entries to re-evaluate the effective value when the
         /// binding produces a new value.
         /// binding produces a new value.
@@ -362,6 +357,32 @@ namespace Avalonia.PropertyStore
             }
             }
         }
         }
 
 
+        /// <summary>
+        /// Called by the parent value store when its inheritance ancestor changes.
+        /// </summary>
+        /// <param name="ancestor">The new inheritance ancestor.</param>
+        public void OnInheritanceAncestorChanged(ValueStore? ancestor)
+        {
+            if (ancestor != this)
+            {
+                InheritanceAncestor = ancestor;
+                if (_inheritedValueCount > 0)
+                    return;
+            }
+
+            var children = Owner.GetInheritanceChildren();
+
+            if (children is null)
+                return;
+
+            var count = children.Count;
+
+            for (var i = 0; i < count; ++i)
+            {
+                children[i].GetValueStore().OnInheritanceAncestorChanged(ancestor);
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Called by <see cref="EffectiveValue{T}"/> when an property with inheritance enabled
         /// Called by <see cref="EffectiveValue{T}"/> when an property with inheritance enabled
         /// changes its value on this value store.
         /// changes its value on this value store.
@@ -370,7 +391,7 @@ namespace Avalonia.PropertyStore
         /// <param name="oldValue">The old value of the property.</param>
         /// <param name="oldValue">The old value of the property.</param>
         /// <param name="value">The effective value instance.</param>
         /// <param name="value">The effective value instance.</param>
         public void OnInheritedEffectiveValueChanged<T>(
         public void OnInheritedEffectiveValueChanged<T>(
-            StyledPropertyBase<T> property, 
+            StyledPropertyBase<T> property,
             T oldValue,
             T oldValue,
             EffectiveValue<T> value)
             EffectiveValue<T> value)
         {
         {
@@ -378,20 +399,14 @@ namespace Avalonia.PropertyStore
 
 
             var children = Owner.GetInheritanceChildren();
             var children = Owner.GetInheritanceChildren();
 
 
-            // If we have children or an existing inheritance frame, then make sure it's owned and
-            // set the value. If we have no children and no inheritance frame then it will be
-            // created when it's needed.
-            if (children is not null || _inheritanceFrame is not null)
-                GetOrCreateInheritanceFrame(true)[property] = value;
+            if (children is null)
+                return;
 
 
-            if (children is not null)
-            {
-                var count = children.Count;
+            var count = children.Count;
 
 
-                for (var i = 0; i < count; ++i)
-                {
-                    children[i].GetValueStore().OnParentInheritedValueChanged(property, oldValue, value.Value);
-                }
+            for (var i = 0; i < count; ++i)
+            {
+                children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, value.Value);
             }
             }
         }
         }
 
 
@@ -404,12 +419,6 @@ namespace Avalonia.PropertyStore
         public void OnInheritedEffectiveValueDisposed<T>(StyledPropertyBase<T> property, T oldValue)
         public void OnInheritedEffectiveValueDisposed<T>(StyledPropertyBase<T> property, T oldValue)
         {
         {
             Debug.Assert(property.Inherits);
             Debug.Assert(property.Inherits);
-            Debug.Assert(_inheritanceFrame is null || _inheritanceFrame.Owner == this);
-
-            if (_inheritanceFrame is null || _inheritanceFrame.Owner != this)
-                return;
-
-            _inheritanceFrame.Remove(property);
 
 
             var children = Owner.GetInheritanceChildren();
             var children = Owner.GetInheritanceChildren();
 
 
@@ -420,7 +429,7 @@ namespace Avalonia.PropertyStore
 
 
                 for (var i = 0; i < count; ++i)
                 for (var i = 0; i < count; ++i)
                 {
                 {
-                    children[i].GetValueStore().OnParentInheritedValueChanged(property, oldValue, defaultValue);
+                    children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, defaultValue);
                 }
                 }
             }
             }
         }
         }
@@ -443,16 +452,20 @@ namespace Avalonia.PropertyStore
             }
             }
         }
         }
 
 
-        public void OnParentInheritedValueChanged<T>(
+        /// <summary>
+        /// Called when an inherited property changes on the value store of the inheritance ancestor.
+        /// </summary>
+        /// <typeparam name="T">The property type.</typeparam>
+        /// <param name="property">The property.</param>
+        /// <param name="oldValue">The old value of the property.</param>
+        /// <param name="newValue">The new value of the property.</param>
+        public void OnAncestorInheritedValueChanged<T>(
             StyledPropertyBase<T> property, 
             StyledPropertyBase<T> property, 
             T oldValue,
             T oldValue,
             T newValue)
             T newValue)
         {
         {
             Debug.Assert(property.Inherits);
             Debug.Assert(property.Inherits);
 
 
-            // Ensure the inheritance frame is created.
-            GetOrCreateInheritanceFrame(false);
-
             // If the inherited value is set locally, propagation stops here.
             // If the inherited value is set locally, propagation stops here.
             if (_effectiveValues is not null && _effectiveValues.ContainsKey(property))
             if (_effectiveValues is not null && _effectiveValues.ContainsKey(property))
                 return;
                 return;
@@ -473,7 +486,7 @@ namespace Avalonia.PropertyStore
 
 
             for (var i = 0; i < count; ++i)
             for (var i = 0; i < count; ++i)
             {
             {
-                children[i].GetValueStore().OnParentInheritedValueChanged(property, oldValue, newValue);
+                children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, newValue);
             }
             }
         }
         }
 
 
@@ -535,33 +548,6 @@ namespace Avalonia.PropertyStore
             frame.SetOwner(this);
             frame.SetOwner(this);
         }
         }
 
 
-        private InheritanceFrame GetOrCreateInheritanceFrame(bool owned)
-        {
-            if (_inheritanceFrame is null)
-            {
-                var parentFrame = Owner.InheritanceParent?.GetValueStore()._inheritanceFrame;
-
-                _inheritanceFrame = owned || parentFrame is null ? 
-                    new(this, parentFrame) : 
-                    parentFrame;
-
-                if (_effectiveValues is not null)
-                {
-                    foreach (var i in _effectiveValues)
-                    {
-                        if (i.Key.Inherits)
-                            _inheritanceFrame[i.Key] = i.Value;
-                    }
-                }
-            }
-            else if (owned && _inheritanceFrame.Owner != this)
-            {
-                _inheritanceFrame = new(this, _inheritanceFrame);
-            }
-
-            return _inheritanceFrame;
-        }
-
         private ImmediateValueFrame GetOrCreateImmediateValueFrame(
         private ImmediateValueFrame GetOrCreateImmediateValueFrame(
             AvaloniaProperty property, 
             AvaloniaProperty property, 
             BindingPriority priority)
             BindingPriority priority)
@@ -600,11 +586,20 @@ namespace Avalonia.PropertyStore
             }
             }
             else
             else
             {
             {
-                _effectiveValues?.Remove(property);
+                RemoveEffectiveValue(property);
                 current.DisposeAndRaiseUnset(this, property);
                 current.DisposeAndRaiseUnset(this, property);
             }
             }
         }
         }
 
 
+        private void AddEffectiveValue(AvaloniaProperty property, EffectiveValue effectiveValue)
+        {
+            _effectiveValues ??= new();
+            _effectiveValues.Add(property, effectiveValue);
+
+            if (property.Inherits && _inheritedValueCount++ == 0)
+                OnInheritanceAncestorChanged(this);
+        }
+
         /// <summary>
         /// <summary>
         /// Adds a new effective value, raises the initial <see cref="AvaloniaObject.PropertyChanged"/>
         /// Adds a new effective value, raises the initial <see cref="AvaloniaObject.PropertyChanged"/>
         /// event and notifies inheritance children if necessary .
         /// event and notifies inheritance children if necessary .
@@ -617,8 +612,7 @@ namespace Avalonia.PropertyStore
         {
         {
             Debug.Assert(priority < BindingPriority.Inherited);
             Debug.Assert(priority < BindingPriority.Inherited);
             var effectiveValue = property.CreateEffectiveValue(Owner);
             var effectiveValue = property.CreateEffectiveValue(Owner);
-            _effectiveValues ??= new();
-            _effectiveValues.Add(property, effectiveValue);
+            AddEffectiveValue(property, effectiveValue);
             effectiveValue.SetAndRaise(this, property, value, priority);
             effectiveValue.SetAndRaise(this, property, value, priority);
         }
         }
 
 
@@ -635,11 +629,35 @@ namespace Avalonia.PropertyStore
             Debug.Assert(priority < BindingPriority.Inherited);
             Debug.Assert(priority < BindingPriority.Inherited);
             var defaultValue = property.GetDefaultValue(Owner.GetType());
             var defaultValue = property.GetDefaultValue(Owner.GetType());
             var effectiveValue = new EffectiveValue<T>(defaultValue, BindingPriority.Unset);
             var effectiveValue = new EffectiveValue<T>(defaultValue, BindingPriority.Unset);
-            _effectiveValues ??= new();
-            _effectiveValues.Add(property, effectiveValue);
+            AddEffectiveValue(property, effectiveValue);
             effectiveValue.SetAndRaise(this, property, value, priority);
             effectiveValue.SetAndRaise(this, property, value, priority);
         }
         }
 
 
+        private bool RemoveEffectiveValue(AvaloniaProperty property)
+        {
+            if (_effectiveValues is not null && _effectiveValues.Remove(property))
+            {
+                if (property.Inherits && --_inheritedValueCount == 0)
+                    OnInheritanceAncestorChanged(InheritanceAncestor?.InheritanceAncestor);
+                return true;
+            }
+
+            return false;
+        }
+
+        private bool RemoveEffectiveValue(AvaloniaProperty property, [NotNullWhen(true)] out EffectiveValue? result)
+        {
+            if (_effectiveValues is not null && _effectiveValues.Remove(property, out result))
+            {
+                if (property.Inherits && --_inheritedValueCount == 0)
+                    OnInheritanceAncestorChanged(InheritanceAncestor?.InheritanceAncestor);
+                return true;
+            }
+
+            result = null;
+            return false;
+        }
+
         /// <summary>
         /// <summary>
         /// Evaluates the current value and base value for a property based on the current frames and optionally
         /// Evaluates the current value and base value for a property based on the current frames and optionally
         /// local values. Does not evaluate inherited values.
         /// local values. Does not evaluate inherited values.
@@ -761,30 +779,6 @@ namespace Avalonia.PropertyStore
             }
             }
         }
         }
 
 
-        private void ParentInheritanceFrameChanged(InheritanceFrame? parent)
-        {
-            if (_inheritanceFrame?.Owner == this)
-            {
-                _inheritanceFrame.SetParent(parent);
-            }
-            else if (_inheritanceFrame != parent)
-            {
-                _inheritanceFrame = parent;
-
-                var children = Owner.GetInheritanceChildren();
-
-                if (children is null)
-                    return;
-
-                var count = children.Count;
-
-                for (var i = 0; i < count; ++i)
-                {
-                    children[i].GetValueStore().ParentInheritanceFrameChanged(parent);
-                }
-            }
-        }
-
         private void ReevaluateEffectiveValues()
         private void ReevaluateEffectiveValues()
         {
         {
         restart:
         restart:
@@ -841,8 +835,7 @@ namespace Avalonia.PropertyStore
                     else
                     else
                     {
                     {
                         var v = property.CreateEffectiveValue(Owner);
                         var v = property.CreateEffectiveValue(Owner);
-                        _effectiveValues ??= new();
-                        _effectiveValues.Add(property, v);
+                        AddEffectiveValue(property, v);
                         v.SetAndRaise(this, entry, priority);
                         v.SetAndRaise(this, entry, priority);
                     }
                     }
 
 
@@ -871,7 +864,7 @@ namespace Avalonia.PropertyStore
                 {
                 {
                     foreach (var v in remove)
                     foreach (var v in remove)
                     {
                     {
-                        if (_effectiveValues.Remove(v, out var e))
+                        if (RemoveEffectiveValue(v, out var e))
                             e.DisposeAndRaiseUnset(this, v);
                             e.DisposeAndRaiseUnset(this, v);
                     }
                     }
                     remove.Dispose();
                     remove.Dispose();
@@ -890,6 +883,25 @@ namespace Avalonia.PropertyStore
             return false;
             return false;
         }
         }
 
 
+        private bool TryGetInheritedValue(
+            AvaloniaProperty property,
+            [NotNullWhen(true)] out EffectiveValue? result)
+        {
+            Debug.Assert(property.Inherits);
+
+            var i = InheritanceAncestor;
+
+            while (i is not null)
+            {
+                if (i.TryGetEffectiveValue(property, out result))
+                    return true;
+                i = i.InheritanceAncestor;
+            }
+
+            result = null;
+            return false;
+        }
+
         private EffectiveValue? GetEffectiveValue(AvaloniaProperty property)
         private EffectiveValue? GetEffectiveValue(AvaloniaProperty property)
         {
         {
             if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var value))
             if (_effectiveValues is not null && _effectiveValues.TryGetValue(property, out var value))

+ 22 - 0
tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs

@@ -146,6 +146,28 @@ namespace Avalonia.Base.UnitTests
             Assert.Equal("changed", child.GetValue(AttachedOwner.AttachedProperty));
             Assert.Equal("changed", child.GetValue(AttachedOwner.AttachedProperty));
         }
         }
 
 
+        [Fact]
+        public void Clearing_Value_In_InheritanceParent_Raises_PropertyChanged()
+        {
+            bool raised = false;
+
+            Class1 parent = new Class1();
+            parent.SetValue(Class1.BazProperty, "changed");
+
+            Class2 child = new Class2 { Parent = parent };
+
+            child.PropertyChanged += (s, e) =>
+                raised = s == child &&
+                         e.Property == Class1.BazProperty &&
+                         (string)e.OldValue == "changed" &&
+                         (string)e.NewValue == "bazdefault";
+
+            parent.ClearValue(Class1.BazProperty);
+
+            Assert.True(raised);
+            Assert.Equal("bazdefault", child.GetValue(Class1.BazProperty));
+        }
+
         [Fact]
         [Fact]
         public void PropertyChanged_Is_Raised_In_Parent_Before_Child()
         public void PropertyChanged_Is_Raised_In_Parent_Before_Child()
         {
         {

+ 117 - 0
tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs

@@ -0,0 +1,117 @@
+using Xunit;
+
+#nullable enable
+
+namespace Avalonia.Base.UnitTests.PropertyStore
+{
+    public class ValueStoreTests_Inheritance
+    {
+        [Fact]
+        public void InheritanceAncestor_Is_Initially_Null()
+        {
+            var parent = new Class1();
+            var child = new Class1 { Parent = parent };
+            var grandchild = new Class1 { Parent = child };
+
+            Assert.Null(parent.GetValueStore().InheritanceAncestor);
+            Assert.Null(child.GetValueStore().InheritanceAncestor);
+            Assert.Null(grandchild.GetValueStore().InheritanceAncestor);
+        }
+
+        [Fact]
+        public void Setting_Value_In_Parent_Updates_InheritanceAncestor()
+        {
+            var parent = new Class1();
+            var child = new Class1 { Parent = parent };
+            var grandchild = new Class1 { Parent = child };
+
+            parent.Foo = "changed";
+
+            var parentStore = parent.GetValueStore();
+            Assert.Null(parentStore.InheritanceAncestor);
+            Assert.Same(parentStore, child.GetValueStore().InheritanceAncestor);
+            Assert.Same(parentStore, grandchild.GetValueStore().InheritanceAncestor);
+        }
+
+        [Fact]
+        public void Setting_Value_In_Parent_Doesnt_Update_Grandchild_InheritanceAncestor_If_Child_Has_Value_Set()
+        {
+            var parent = new Class1();
+            var child = new Class1 { Parent = parent };
+            var grandchild = new Class1 { Parent = child };
+
+            child.Foo = "foochanged";
+            parent.Foo = "changed";
+
+            var parentStore = parent.GetValueStore();
+            Assert.Null(parentStore.InheritanceAncestor);
+            Assert.Same(parentStore, child.GetValueStore().InheritanceAncestor);
+            Assert.Same(child.GetValueStore(), grandchild.GetValueStore().InheritanceAncestor);
+        }
+
+        [Fact]
+        public void Clearing_Value_In_Parent_Updates_InheritanceAncestor()
+        {
+            var parent = new Class1();
+            var child = new Class1 { Parent = parent };
+            var grandchild = new Class1 { Parent = child };
+
+            parent.Foo = "changed";
+            parent.ClearValue(Class1.FooProperty);
+
+            Assert.Null(parent.GetValueStore().InheritanceAncestor);
+            Assert.Null(child.GetValueStore().InheritanceAncestor);
+            Assert.Null(grandchild.GetValueStore().InheritanceAncestor);
+        }
+
+        [Fact]
+        public void Clear_Value_In_Parent_Doesnt_Update_Grandchild_InheritanceAncestor_If_Child_Has_Value_Set()
+        {
+            var parent = new Class1();
+            var child = new Class1 { Parent = parent };
+            var grandchild = new Class1 { Parent = child };
+
+            child.Foo = "foochanged";
+            parent.Foo = "changed";
+            parent.ClearValue(Class1.FooProperty);
+
+            Assert.Null(parent.GetValueStore().InheritanceAncestor);
+            Assert.Null(child.GetValueStore().InheritanceAncestor);
+            Assert.Same(child.GetValueStore(), grandchild.GetValueStore().InheritanceAncestor);
+        }
+
+        [Fact]
+        public void Adding_Child_Sets_InheritanceAncestor()
+        {
+            var parent = new Class1();
+            var child = new Class1();
+            var grandchild = new Class1 { Parent = child };
+
+            parent.Foo = "changed";
+            child.Parent = parent;
+
+            var parentStore = parent.GetValueStore();
+            Assert.Null(parentStore.InheritanceAncestor);
+            Assert.Same(parentStore, child.GetValueStore().InheritanceAncestor);
+            Assert.Same(parentStore, grandchild.GetValueStore().InheritanceAncestor);
+        }
+
+        private class Class1 : AvaloniaObject
+        {
+            public static readonly StyledProperty<string> FooProperty =
+                AvaloniaProperty.Register<Class1, string>("Foo", "foodefault", inherits: true);
+
+            public string Foo
+            {
+                get => GetValue(FooProperty);
+                set => SetValue(FooProperty, value);
+            }
+
+            public Class1? Parent
+            {
+                get { return (Class1?)InheritanceParent; }
+                set { InheritanceParent = value; }
+            }
+        }
+    }
+}