Browse Source

Fix DataContext notifications.

refactor/style-priorities
Steven Kirk 4 years ago
parent
commit
a49a01c94b
  1. 35
      src/Avalonia.Base/AvaloniaObject.cs
  2. 2
      src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs
  3. 35
      src/Avalonia.Base/PropertyStore/PropertyNotifying.cs
  4. 4
      src/Avalonia.Base/PropertyStore/ValueStore.cs
  5. 51
      tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs

35
src/Avalonia.Base/AvaloniaObject.cs

@ -596,32 +596,21 @@ namespace Avalonia
BindingPriority priority,
bool isEffectiveValue)
{
if (isEffectiveValue)
property.Notifying?.Invoke(this, true);
try
{
var e = new AvaloniaPropertyChangedEventArgs<T>(
this,
property,
oldValue,
newValue,
priority,
isEffectiveValue);
var e = new AvaloniaPropertyChangedEventArgs<T>(
this,
property,
oldValue,
newValue,
priority,
isEffectiveValue);
OnPropertyChangedCore(e);
OnPropertyChangedCore(e);
if (isEffectiveValue)
{
property.NotifyChanged(e);
_propertyChanged?.Invoke(this, e);
_inpcChanged?.Invoke(this, new PropertyChangedEventArgs(property.Name));
}
}
finally
if (isEffectiveValue)
{
if (isEffectiveValue)
property.Notifying?.Invoke(this, false);
property.NotifyChanged(e);
_propertyChanged?.Invoke(this, e);
_inpcChanged?.Invoke(this, new PropertyChangedEventArgs(property.Name));
}
}

2
src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs

@ -133,6 +133,7 @@ namespace Avalonia.PropertyStore
if (valueChanged)
{
using var notifying = PropertyNotifying.Start(owner.Owner, property);
owner.Owner.RaisePropertyChanged(property, oldValue, Value, Priority, true);
if (property.Inherits)
owner.OnInheritedEffectiveValueChanged(property, oldValue, this);
@ -200,6 +201,7 @@ namespace Avalonia.PropertyStore
if (valueChanged)
{
using var notifying = PropertyNotifying.Start(owner.Owner, property);
owner.Owner.RaisePropertyChanged(property, oldValue, Value, Priority, true);
if (property.Inherits)
owner.OnInheritedEffectiveValueChanged(property, oldValue, this);

35
src/Avalonia.Base/PropertyStore/PropertyNotifying.cs

@ -0,0 +1,35 @@
using System;
using System.Diagnostics;
namespace Avalonia.PropertyStore
{
/// <summary>
/// Raises <see cref="AvaloniaProperty.Notifying"/> where necessary.
/// </summary>
/// <remarks>
/// Uses the disposable pattern to ensure that the closing Notifying call is made even in the
/// presence of exceptions.
/// </remarks>
internal readonly struct PropertyNotifying : IDisposable
{
private readonly AvaloniaObject _owner;
private readonly AvaloniaProperty _property;
private PropertyNotifying(AvaloniaObject owner, AvaloniaProperty property)
{
Debug.Assert(property.Notifying is not null);
_owner = owner;
_property = property;
_property.Notifying!(owner, true);
}
public void Dispose() => _property.Notifying!(_owner, false);
public static PropertyNotifying? Start(AvaloniaObject owner, AvaloniaProperty property)
{
if (property.Notifying is null)
return null;
return new PropertyNotifying(owner, property);
}
}
}

4
src/Avalonia.Base/PropertyStore/ValueStore.cs

@ -476,6 +476,8 @@ namespace Avalonia.PropertyStore
if (_effectiveValues is not null && _effectiveValues.ContainsKey(property))
return;
using var notifying = PropertyNotifying.Start(Owner, property);
Owner.RaisePropertyChanged(
property,
oldValue,
@ -769,6 +771,8 @@ namespace Avalonia.PropertyStore
if (_effectiveValues?.ContainsKey(property) == true)
return;
using var notifying = PropertyNotifying.Start(Owner, property);
// Raise PropertyChanged on this object if necessary.
(oldValue ?? newValue!).RaiseInheritedValueChanged(Owner, property, oldValue, newValue);

51
tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs

@ -470,6 +470,57 @@ namespace Avalonia.Base.UnitTests.Styling
called);
}
[Fact]
public void DataContext_Notifications_Should_Be_Called_In_Correct_Order_When_Setting_Parent()
{
var root = new TestStackPanel
{
Name = "root",
DataContext = "foo",
};
var children = new[]
{
new TestControl
{
Name = "a1",
Child = new TestControl
{
Name = "b1",
}
},
new TestControl
{
Name = "a2",
DataContext = "foo",
},
};
var called = new List<string>();
foreach (IDataContextEvents c in new[] { children[0], children[0].Child, children[1] })
{
c.DataContextBeginUpdate += (s, e) => called.Add("begin " + ((StyledElement)s).Name);
c.DataContextChanged += (s, e) => called.Add("changed " + ((StyledElement)s).Name);
c.DataContextEndUpdate += (s, e) => called.Add("end " + ((StyledElement)s).Name);
}
root.Children.AddRange(children);
Assert.Equal(
new[]
{
"begin a1",
"begin b1",
"changed a1",
"changed b1",
"end b1",
"end a1",
},
called);
}
[Fact]
public void Resources_Owner_Is_Set()
{

Loading…
Cancel
Save