Browse Source

Fix order of inherited property changed events.

Previously `PropertyChanged` was raised on the child before the parent because the inherited value change was being notified by listening to the parent `PropertyChanged` event, and this event handler was added first.

Added an `InheritablePropertyChanged` event which will be called only after all other property changed events have been raised and only for inheritable properties.
Steven Kirk 6 years ago
parent
commit
f75f85af2b

+ 18 - 19
src/Avalonia.Base/AvaloniaObject.cs

@@ -22,27 +22,11 @@ namespace Avalonia
     /// </remarks>
     public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged
     {
-        /// <summary>
-        /// The parent object that inherited values are inherited from.
-        /// </summary>
         private IAvaloniaObject _inheritanceParent;
-
-        /// <summary>
-        /// Maintains a list of direct property binding subscriptions so that the binding source
-        /// doesn't get collected.
-        /// </summary>
         private List<DirectBindingSubscription> _directBindings;
-
-        /// <summary>
-        /// Event handler for <see cref="INotifyPropertyChanged"/> implementation.
-        /// </summary>
         private PropertyChangedEventHandler _inpcChanged;
-
-        /// <summary>
-        /// Event handler for <see cref="PropertyChanged"/> implementation.
-        /// </summary>
         private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged;
-
+        private EventHandler<AvaloniaPropertyChangedEventArgs> _inheritablePropertyChanged;
         private ValueStore _values;
         private ValueStore Values => _values ?? (_values = new ValueStore(this));
 
@@ -98,6 +82,15 @@ namespace Avalonia
             remove { _inpcChanged -= value; }
         }
 
+        /// <summary>
+        /// Raised when an inheritable <see cref="AvaloniaProperty"/> value changes on this object.
+        /// </summary>
+        event EventHandler<AvaloniaPropertyChangedEventArgs> IAvaloniaObject.InheritablePropertyChanged
+        {
+            add { _inheritablePropertyChanged += value; }
+            remove { _inheritablePropertyChanged -= value; }
+        }
+
         /// <summary>
         /// Gets or sets the parent object that inherited <see cref="AvaloniaProperty"/> values
         /// are inherited from.
@@ -118,8 +111,9 @@ namespace Avalonia
                 {
                     if (_inheritanceParent != null)
                     {
-                        _inheritanceParent.PropertyChanged -= ParentPropertyChanged;
+                        _inheritanceParent.InheritablePropertyChanged -= ParentPropertyChanged;
                     }
+
                     var properties = AvaloniaPropertyRegistry.Instance.GetRegistered(this)
                         .Concat(AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(this.GetType()));
                     var inherited = (from property in properties
@@ -144,7 +138,7 @@ namespace Avalonia
 
                     if (_inheritanceParent != null)
                     {
-                        _inheritanceParent.PropertyChanged += ParentPropertyChanged;
+                        _inheritanceParent.InheritablePropertyChanged += ParentPropertyChanged;
                     }
                 }
             }
@@ -509,6 +503,11 @@ namespace Avalonia
                     PropertyChangedEventArgs e2 = new PropertyChangedEventArgs(property.Name);
                     _inpcChanged(this, e2);
                 }
+
+                if (property.Inherits)
+                {
+                    _inheritablePropertyChanged?.Invoke(this, e);
+                }
             }
             finally
             {

+ 6 - 1
src/Avalonia.Base/IAvaloniaObject.cs

@@ -16,6 +16,11 @@ namespace Avalonia
         /// </summary>
         event EventHandler<AvaloniaPropertyChangedEventArgs> PropertyChanged;
 
+        /// <summary>
+        /// Raised when an inheritable <see cref="AvaloniaProperty"/> value changes on this object.
+        /// </summary>
+        event EventHandler<AvaloniaPropertyChangedEventArgs> InheritablePropertyChanged;
+
         /// <summary>
         /// Gets a <see cref="AvaloniaProperty"/> value.
         /// </summary>
@@ -97,4 +102,4 @@ namespace Avalonia
             IObservable<T> source,
             BindingPriority priority = BindingPriority.LocalValue);
     }
-}
+}

+ 1 - 0
tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs

@@ -89,6 +89,7 @@ namespace Avalonia.Styling.UnitTests
             }
 
             public event EventHandler<AvaloniaPropertyChangedEventArgs> PropertyChanged;
+            public event EventHandler<AvaloniaPropertyChangedEventArgs> InheritablePropertyChanged;
             public event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree;
             public event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree;
 

+ 1 - 0
tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs

@@ -119,6 +119,7 @@ namespace Avalonia.Styling.UnitTests
             }
 
             public event EventHandler<AvaloniaPropertyChangedEventArgs> PropertyChanged;
+            public event EventHandler<AvaloniaPropertyChangedEventArgs> InheritablePropertyChanged;
             public event EventHandler<LogicalTreeAttachmentEventArgs> AttachedToLogicalTree;
             public event EventHandler<LogicalTreeAttachmentEventArgs> DetachedFromLogicalTree;
 

+ 1 - 0
tests/Avalonia.Styling.UnitTests/TestControlBase.cs

@@ -19,6 +19,7 @@ namespace Avalonia.Styling.UnitTests
 
 #pragma warning disable CS0067 // Event not used
         public event EventHandler<AvaloniaPropertyChangedEventArgs> PropertyChanged;
+        public event EventHandler<AvaloniaPropertyChangedEventArgs> InheritablePropertyChanged;
 #pragma warning restore CS0067
 
         public string Name { get; set; }

+ 1 - 0
tests/Avalonia.Styling.UnitTests/TestTemplatedControl.cs

@@ -12,6 +12,7 @@ namespace Avalonia.Styling.UnitTests
     public abstract class TestTemplatedControl : ITemplatedControl, IStyleable
     {
         public event EventHandler<AvaloniaPropertyChangedEventArgs> PropertyChanged;
+        public event EventHandler<AvaloniaPropertyChangedEventArgs> InheritablePropertyChanged;
 
         public abstract Classes Classes
         {