From f75f85af2bd6d81b2deb9f571a2abd8e823441a3 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 20 Jan 2019 01:19:25 +0100 Subject: [PATCH] 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. --- src/Avalonia.Base/AvaloniaObject.cs | 37 +++++++++---------- src/Avalonia.Base/IAvaloniaObject.cs | 7 +++- .../SelectorTests_Child.cs | 1 + .../SelectorTests_Descendent.cs | 1 + .../TestControlBase.cs | 1 + .../TestTemplatedControl.cs | 1 + 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 7e8d733f1b..28148f6568 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -22,27 +22,11 @@ namespace Avalonia /// public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged { - /// - /// The parent object that inherited values are inherited from. - /// private IAvaloniaObject _inheritanceParent; - - /// - /// Maintains a list of direct property binding subscriptions so that the binding source - /// doesn't get collected. - /// private List _directBindings; - - /// - /// Event handler for implementation. - /// private PropertyChangedEventHandler _inpcChanged; - - /// - /// Event handler for implementation. - /// private EventHandler _propertyChanged; - + private EventHandler _inheritablePropertyChanged; private ValueStore _values; private ValueStore Values => _values ?? (_values = new ValueStore(this)); @@ -98,6 +82,15 @@ namespace Avalonia remove { _inpcChanged -= value; } } + /// + /// Raised when an inheritable value changes on this object. + /// + event EventHandler IAvaloniaObject.InheritablePropertyChanged + { + add { _inheritablePropertyChanged += value; } + remove { _inheritablePropertyChanged -= value; } + } + /// /// Gets or sets the parent object that inherited 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 { diff --git a/src/Avalonia.Base/IAvaloniaObject.cs b/src/Avalonia.Base/IAvaloniaObject.cs index c11f8ada7e..5a3829167a 100644 --- a/src/Avalonia.Base/IAvaloniaObject.cs +++ b/src/Avalonia.Base/IAvaloniaObject.cs @@ -16,6 +16,11 @@ namespace Avalonia /// event EventHandler PropertyChanged; + /// + /// Raised when an inheritable value changes on this object. + /// + event EventHandler InheritablePropertyChanged; + /// /// Gets a value. /// @@ -97,4 +102,4 @@ namespace Avalonia IObservable source, BindingPriority priority = BindingPriority.LocalValue); } -} \ No newline at end of file +} diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs index 0561837ad1..c6eeb1ec0e 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs @@ -89,6 +89,7 @@ namespace Avalonia.Styling.UnitTests } public event EventHandler PropertyChanged; + public event EventHandler InheritablePropertyChanged; public event EventHandler AttachedToLogicalTree; public event EventHandler DetachedFromLogicalTree; diff --git a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs index 56dad13186..aef539becd 100644 --- a/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs +++ b/tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs @@ -119,6 +119,7 @@ namespace Avalonia.Styling.UnitTests } public event EventHandler PropertyChanged; + public event EventHandler InheritablePropertyChanged; public event EventHandler AttachedToLogicalTree; public event EventHandler DetachedFromLogicalTree; diff --git a/tests/Avalonia.Styling.UnitTests/TestControlBase.cs b/tests/Avalonia.Styling.UnitTests/TestControlBase.cs index 82be755a39..d9fabf6f5d 100644 --- a/tests/Avalonia.Styling.UnitTests/TestControlBase.cs +++ b/tests/Avalonia.Styling.UnitTests/TestControlBase.cs @@ -19,6 +19,7 @@ namespace Avalonia.Styling.UnitTests #pragma warning disable CS0067 // Event not used public event EventHandler PropertyChanged; + public event EventHandler InheritablePropertyChanged; #pragma warning restore CS0067 public string Name { get; set; } diff --git a/tests/Avalonia.Styling.UnitTests/TestTemplatedControl.cs b/tests/Avalonia.Styling.UnitTests/TestTemplatedControl.cs index 03b2f03bf2..e92ac36e8f 100644 --- a/tests/Avalonia.Styling.UnitTests/TestTemplatedControl.cs +++ b/tests/Avalonia.Styling.UnitTests/TestTemplatedControl.cs @@ -12,6 +12,7 @@ namespace Avalonia.Styling.UnitTests public abstract class TestTemplatedControl : ITemplatedControl, IStyleable { public event EventHandler PropertyChanged; + public event EventHandler InheritablePropertyChanged; public abstract Classes Classes {