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.
pull/2264/head
Steven Kirk 7 years ago
parent
commit
f75f85af2b
  1. 37
      src/Avalonia.Base/AvaloniaObject.cs
  2. 7
      src/Avalonia.Base/IAvaloniaObject.cs
  3. 1
      tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs
  4. 1
      tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs
  5. 1
      tests/Avalonia.Styling.UnitTests/TestControlBase.cs
  6. 1
      tests/Avalonia.Styling.UnitTests/TestTemplatedControl.cs

37
src/Avalonia.Base/AvaloniaObject.cs

@ -22,27 +22,11 @@ namespace Avalonia
/// </remarks> /// </remarks>
public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged
{ {
/// <summary>
/// The parent object that inherited values are inherited from.
/// </summary>
private IAvaloniaObject _inheritanceParent; 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; private List<DirectBindingSubscription> _directBindings;
/// <summary>
/// Event handler for <see cref="INotifyPropertyChanged"/> implementation.
/// </summary>
private PropertyChangedEventHandler _inpcChanged; private PropertyChangedEventHandler _inpcChanged;
/// <summary>
/// Event handler for <see cref="PropertyChanged"/> implementation.
/// </summary>
private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged; private EventHandler<AvaloniaPropertyChangedEventArgs> _propertyChanged;
private EventHandler<AvaloniaPropertyChangedEventArgs> _inheritablePropertyChanged;
private ValueStore _values; private ValueStore _values;
private ValueStore Values => _values ?? (_values = new ValueStore(this)); private ValueStore Values => _values ?? (_values = new ValueStore(this));
@ -98,6 +82,15 @@ namespace Avalonia
remove { _inpcChanged -= value; } 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> /// <summary>
/// Gets or sets the parent object that inherited <see cref="AvaloniaProperty"/> values /// Gets or sets the parent object that inherited <see cref="AvaloniaProperty"/> values
/// are inherited from. /// are inherited from.
@ -118,8 +111,9 @@ namespace Avalonia
{ {
if (_inheritanceParent != null) if (_inheritanceParent != null)
{ {
_inheritanceParent.PropertyChanged -= ParentPropertyChanged; _inheritanceParent.InheritablePropertyChanged -= ParentPropertyChanged;
} }
var properties = AvaloniaPropertyRegistry.Instance.GetRegistered(this) var properties = AvaloniaPropertyRegistry.Instance.GetRegistered(this)
.Concat(AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(this.GetType())); .Concat(AvaloniaPropertyRegistry.Instance.GetRegisteredAttached(this.GetType()));
var inherited = (from property in properties var inherited = (from property in properties
@ -144,7 +138,7 @@ namespace Avalonia
if (_inheritanceParent != null) if (_inheritanceParent != null)
{ {
_inheritanceParent.PropertyChanged += ParentPropertyChanged; _inheritanceParent.InheritablePropertyChanged += ParentPropertyChanged;
} }
} }
} }
@ -509,6 +503,11 @@ namespace Avalonia
PropertyChangedEventArgs e2 = new PropertyChangedEventArgs(property.Name); PropertyChangedEventArgs e2 = new PropertyChangedEventArgs(property.Name);
_inpcChanged(this, e2); _inpcChanged(this, e2);
} }
if (property.Inherits)
{
_inheritablePropertyChanged?.Invoke(this, e);
}
} }
finally finally
{ {

7
src/Avalonia.Base/IAvaloniaObject.cs

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

1
tests/Avalonia.Styling.UnitTests/SelectorTests_Child.cs

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

1
tests/Avalonia.Styling.UnitTests/SelectorTests_Descendent.cs

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

1
tests/Avalonia.Styling.UnitTests/TestControlBase.cs

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

1
tests/Avalonia.Styling.UnitTests/TestTemplatedControl.cs

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

Loading…
Cancel
Save