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 Subject<IStyleable> _styleDetach = new Subject<IStyleable>();
private ITemplatedControl _templatedParent; private ITemplatedControl _templatedParent;
private bool _dataContextUpdating; private bool _dataContextUpdating;
private bool _notifyingResourcesChanged;
/// <summary> /// <summary>
/// Initializes static members of the <see cref="StyledElement"/> class. /// Initializes static members of the <see cref="StyledElement"/> class.
@ -235,6 +236,7 @@ namespace Avalonia
} }
_styles.ResourcesChanged += ThisResourcesChanged; _styles.ResourcesChanged += ThisResourcesChanged;
NotifyResourcesChanged(new ResourcesChangedEventArgs());
} }
} }
} }
@ -253,6 +255,7 @@ namespace Avalonia
if (_resources != null) if (_resources != null)
{ {
(_resources as ISetResourceParent)?.SetParent(null);
hadResources = _resources.Count > 0; hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ThisResourcesChanged; _resources.ResourcesChanged -= ThisResourcesChanged;
} }
@ -260,9 +263,14 @@ namespace Avalonia
_resources = value; _resources = value;
_resources.ResourcesChanged += ThisResourcesChanged; _resources.ResourcesChanged += ThisResourcesChanged;
if (value is ISetResourceParent setParent && setParent.ResourceParent == null)
{
setParent.SetParent(this);
}
if (hadResources || _resources.Count > 0) if (hadResources || _resources.Count > 0)
{ {
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs()); NotifyResourcesChanged(new ResourcesChangedEventArgs());
} }
} }
} }
@ -407,10 +415,7 @@ namespace Avalonia
} }
/// <inheritdoc/> /// <inheritdoc/>
void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e) void ILogical.NotifyResourcesChanged(ResourcesChangedEventArgs e) => NotifyResourcesChanged(e);
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
/// <inheritdoc/> /// <inheritdoc/>
bool IResourceProvider.TryGetResource(object key, out object value) bool IResourceProvider.TryGetResource(object key, out object value)
@ -456,7 +461,8 @@ namespace Avalonia
{ {
Parent.ResourcesChanged += ThisResourcesChanged; Parent.ResourcesChanged += ThisResourcesChanged;
} }
((ILogical)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
NotifyResourcesChanged(new ResourcesChangedEventArgs());
if (Parent is ILogicalRoot || Parent?.IsAttachedToLogicalTree == true || this is ILogicalRoot) 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) 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 IResourceDictionary _resources;
private AvaloniaList<IStyle> _styles = new AvaloniaList<IStyle>(); private AvaloniaList<IStyle> _styles = new AvaloniaList<IStyle>();
private Dictionary<Type, List<IStyle>> _cache; private Dictionary<Type, List<IStyle>> _cache;
private bool _notifyingResourcesChanged;
public Styles() public Styles()
{ {
@ -38,7 +39,7 @@ namespace Avalonia.Styling
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
} }
x.ResourcesChanged += SubResourceChanged; x.ResourcesChanged += NotifyResourcesChanged;
_cache = null; _cache = null;
}, },
x => x =>
@ -54,7 +55,7 @@ namespace Avalonia.Styling
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs()); ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
} }
x.ResourcesChanged -= SubResourceChanged; x.ResourcesChanged -= NotifyResourcesChanged;
_cache = null; _cache = null;
}, },
() => { }); () => { });
@ -90,11 +91,11 @@ namespace Avalonia.Styling
if (_resources != null) if (_resources != null)
{ {
hadResources = _resources.Count > 0; hadResources = _resources.Count > 0;
_resources.ResourcesChanged -= ResourceDictionaryChanged; _resources.ResourcesChanged -= NotifyResourcesChanged;
} }
_resources = value; _resources = value;
_resources.ResourcesChanged += ResourceDictionaryChanged; _resources.ResourcesChanged += NotifyResourcesChanged;
if (hadResources || _resources.Count > 0) if (hadResources || _resources.Count > 0)
{ {
@ -261,34 +262,35 @@ namespace Avalonia.Styling
/// <inheritdoc/> /// <inheritdoc/>
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e) 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) NotifyResourcesChanged(e);
{
(child as ISetResourceParent)?.ParentResourcesChanged(e);
}
ResourcesChanged?.Invoke(this, 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); (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); 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] [Fact]
public void Changing_Parent_Notifies_Resources_ParentResourcesChanged() public void Changing_Parent_Notifies_Resources_ParentResourcesChanged()
{ {
@ -505,6 +535,36 @@ namespace Avalonia.Styling.UnitTests
Times.Once); 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] [Fact]
public void Changing_Parent_Notifies_Styles_ParentResourcesChanged() public void Changing_Parent_Notifies_Styles_ParentResourcesChanged()
{ {

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

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

Loading…
Cancel
Save