Browse Source

Pass ResourceChanged messages to child styles.

Fixes #3590.
pull/3597/head
Steven Kirk 6 years ago
parent
commit
4f2c215993
  1. 40
      src/Avalonia.Styling/StyledElement.cs
  2. 40
      src/Avalonia.Styling/Styling/Styles.cs
  3. 60
      tests/Avalonia.Styling.UnitTests/StyledElementTests.cs
  4. 26
      tests/Avalonia.Styling.UnitTests/StylesTests.cs

40
src/Avalonia.Styling/StyledElement.cs

@ -67,6 +67,7 @@ namespace Avalonia
private Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
private ITemplatedControl _templatedParent;
private bool _dataContextUpdating;
private bool _notifyingResourcesChanged;
/// <summary>
/// Initializes static members of the <see cref="StyledElement"/> class.
@ -235,6 +236,7 @@ namespace Avalonia
}
_styles.ResourcesChanged += ThisResourcesChanged;
NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
@ -253,6 +255,7 @@ namespace Avalonia
if (_resources != null)
{
(_resources as ISetResourceParent)?.SetParent(null);
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ThisResourcesChanged;
}
@ -260,9 +263,14 @@ namespace Avalonia
_resources = value;
_resources.ResourcesChanged += ThisResourcesChanged;
if (value is ISetResourceParent setParent && setParent.ResourceParent == null)
{
setParent.SetParent(this);
}
if (hadResources || _resources.Count > 0)
{
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
NotifyResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
@ -407,10 +415,7 @@ namespace Avalonia
}
/// <inheritdoc/>
void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e) => NotifyResourcesChanged(e);
/// <inheritdoc/>
bool IResourceProvider.TryGetResource(object key, out object value)
@ -456,7 +461,8 @@ namespace Avalonia
{
Parent.ResourcesChanged += ThisResourcesChanged;
}
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
NotifyResourcesChanged(new ResourcesChangedEventArgs());
if (Parent is ILogicalRoot || Parent?.IsAttachedToLogicalTree == true || this is ILogicalRoot)
{
@ -721,9 +727,29 @@ namespace Avalonia
}
}
private void NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
if (_notifyingResourcesChanged)
{
return;
}
try
{
_notifyingResourcesChanged = true;
(_resources as ISetResourceParent)?.ParentResourcesChanged(e);
(_styles as ISetResourceParent)?.ParentResourcesChanged(e);
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
finally
{
_notifyingResourcesChanged = false;
}
}
private void ThisResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
((ILogical)this).NotifyResourcesChanged(e);
NotifyResourcesChanged(e);
}
}
}

40
src/Avalonia.Styling/Styling/Styles.cs

@ -20,6 +20,7 @@ namespace Avalonia.Styling
private IResourceDictionary _resources;
private AvaloniaList<IStyle> _styles = new AvaloniaList<IStyle>();
private Dictionary<Type, List<IStyle>> _cache;
private bool _notifyingResourcesChanged;
public Styles()
{
@ -38,7 +39,7 @@ namespace Avalonia.Styling
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
x.ResourcesChanged += SubResourceChanged;
x.ResourcesChanged += NotifyResourcesChanged;
_cache = null;
},
x =>
@ -54,7 +55,7 @@ namespace Avalonia.Styling
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
x.ResourcesChanged -= SubResourceChanged;
x.ResourcesChanged -= NotifyResourcesChanged;
_cache = null;
},
() => { });
@ -90,11 +91,11 @@ namespace Avalonia.Styling
if (_resources != null)
{
hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ResourceDictionaryChanged;
_resources.ResourcesChanged -= NotifyResourcesChanged;
}
_resources = value;
_resources.ResourcesChanged += ResourceDictionaryChanged;
_resources.ResourcesChanged += NotifyResourcesChanged;
if (hadResources || _resources.Count > 0)
{
@ -261,34 +262,35 @@ namespace Avalonia.Styling
/// <inheritdoc/>
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
NotifyResourcesChanged(e);
}
private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs e)
private void NotifyResourcesChanged(object sender, ResourcesChangedEventArgs e)
{
foreach (var child in this)
{
(child as ISetResourceParent)?.ParentResourcesChanged(e);
}
ResourcesChanged?.Invoke(this, e);
NotifyResourcesChanged(e);
}
private void SubResourceChanged(object sender, ResourcesChangedEventArgs e)
private void NotifyResourcesChanged(ResourcesChangedEventArgs e)
{
var foundSource = false;
if (_notifyingResourcesChanged)
{
return;
}
foreach (var child in this)
try
{
if (foundSource)
_notifyingResourcesChanged = true;
foreach (var child in this)
{
(child as ISetResourceParent)?.ParentResourcesChanged(e);
}
foundSource |= child == sender;
ResourcesChanged?.Invoke(this, e);
}
finally
{
_notifyingResourcesChanged = false;
}
ResourcesChanged?.Invoke(this, e);
}
}
}

60
tests/Avalonia.Styling.UnitTests/StyledElementTests.cs

@ -489,6 +489,36 @@ namespace Avalonia.Styling.UnitTests
called);
}
[Fact]
public void Resources_Parent_Is_Set()
{
var target = new TestControl();
Assert.Same(target, ((IResourceNode)target.Resources).ResourceParent);
}
[Fact]
public void Assigned_Resources_Parent_Is_Set()
{
var resources = new ResourceDictionary();
var target = new TestControl { Resources = resources };
Assert.Same(target, ((IResourceNode)resources).ResourceParent);
}
[Fact]
public void Assigning_Resources_Raises_ResourcesChanged()
{
var resources = new ResourceDictionary { { "foo", "bar" } };
var target = new TestControl();
var raised = 0;
target.ResourcesChanged += (s, e) => ++raised;
target.Resources = resources;
Assert.Equal(1, raised);
}
[Fact]
public void Changing_Parent_Notifies_Resources_ParentResourcesChanged()
{
@ -505,6 +535,36 @@ namespace Avalonia.Styling.UnitTests
Times.Once);
}
[Fact]
public void Styles_Parent_Is_Set()
{
var target = new TestControl();
Assert.Same(target, ((IResourceNode)target.Styles).ResourceParent);
}
[Fact]
public void Assigned_Styles_Parent_Is_Set()
{
var styles = new Styles();
var target = new TestControl { Styles = styles };
Assert.Same(target, ((IResourceNode)styles).ResourceParent);
}
[Fact]
public void Assigning_Styles_Raises_ResourcesChanged()
{
var styles = new Styles { Resources = { { "foo", "bar" } } };
var target = new TestControl();
var raised = 0;
target.ResourcesChanged += (s, e) => ++raised;
target.Styles = styles;
Assert.Equal(1, raised);
}
[Fact]
public void Changing_Parent_Notifies_Styles_ParentResourcesChanged()
{

26
tests/Avalonia.Styling.UnitTests/StylesTests.cs

@ -3,6 +3,7 @@
using System;
using Avalonia.Controls;
using Moq;
using Xunit;
namespace Avalonia.Styling.UnitTests
@ -76,7 +77,7 @@ namespace Avalonia.Styling.UnitTests
}
[Fact]
public void Adding_Resource_To_Younger_Sibling_Style_Should_Raise_ResourceChanged()
public void Adding_Resource_To_Sibling_Style_Should_Raise_ResourceChanged()
{
Style style1;
Style style2;
@ -95,25 +96,20 @@ namespace Avalonia.Styling.UnitTests
}
[Fact]
public void Adding_Resource_To_Older_Sibling_Style_Should_Raise_ResourceChanged()
public void ParentResourcesChanged_Should_Be_Propagated_To_Children()
{
Style style1;
Style style2;
var target = new Styles
{
(style1 = new Style()),
(style2 = new Style()),
};
var raised = false;
var childStyle = new Mock<IStyle>();
var setResourceParent = childStyle.As<ISetResourceParent>();
var target = new Styles { childStyle.Object };
style1.ResourcesChanged += (_, __) => raised = true;
style2.Resources.Add("foo", "bar");
setResourceParent.ResetCalls();
((ISetResourceParent)target).ParentResourcesChanged(new ResourcesChangedEventArgs());
Assert.False(raised);
setResourceParent.Verify(x => x.ParentResourcesChanged(
It.IsAny<ResourcesChangedEventArgs>()),
Times.Once);
}
[Fact]
public void Finds_Resource_In_Merged_Dictionary()
{

Loading…
Cancel
Save