diff --git a/src/Avalonia.Controls/Application.cs b/src/Avalonia.Controls/Application.cs
index fda97e259c..4533855b73 100644
--- a/src/Avalonia.Controls/Application.cs
+++ b/src/Avalonia.Controls/Application.cs
@@ -125,6 +125,9 @@ namespace Avalonia
///
IStyleHost IStyleHost.StylingParent => null;
+ ///
+ bool IResourceProvider.HasResources => _resources?.Count > 0;
+
///
/// Initializes the application by loading XAML etc.
///
diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs
index 81acf1a204..c93742b7f6 100644
--- a/src/Avalonia.Controls/Control.cs
+++ b/src/Avalonia.Controls/Control.cs
@@ -382,6 +382,9 @@ namespace Avalonia.Controls
///
IAvaloniaReadOnlyList ILogical.LogicalChildren => LogicalChildren;
+ ///
+ bool IResourceProvider.HasResources => _resources?.Count > 0 || Styles.HasResources;
+
///
IAvaloniaReadOnlyList IStyleable.Classes => Classes;
diff --git a/src/Avalonia.Styling/Controls/IResourceProvider.cs b/src/Avalonia.Styling/Controls/IResourceProvider.cs
index 9308b7a18d..e3ea2e1aab 100644
--- a/src/Avalonia.Styling/Controls/IResourceProvider.cs
+++ b/src/Avalonia.Styling/Controls/IResourceProvider.cs
@@ -12,6 +12,11 @@ namespace Avalonia.Controls
///
event EventHandler ResourcesChanged;
+ ///
+ /// Gets a value indicating whether the provider has resources.
+ ///
+ bool HasResources { get; }
+
///
/// Tries to find a resource within the element.
///
diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs
index 1ea99a42ac..b095f38035 100644
--- a/src/Avalonia.Styling/Styling/Style.cs
+++ b/src/Avalonia.Styling/Styling/Style.cs
@@ -66,6 +66,9 @@ namespace Avalonia.Styling
[Content]
public IList Setters { get; set; } = new List();
+ ///
+ bool IResourceProvider.HasResources => _resources?.Count > 0;
+
///
/// Attaches the style to a control if the style's selector matches.
///
diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs
index 4ce41f6f46..47647d726e 100644
--- a/src/Avalonia.Styling/Styling/Styles.cs
+++ b/src/Avalonia.Styling/Styling/Styles.cs
@@ -20,14 +20,33 @@ namespace Avalonia.Styling
{
ResetBehavior = ResetBehavior.Remove;
this.ForEachItem(
- x => x.ResourcesChanged += SubResourceChanged,
- x => x.ResourcesChanged -= SubResourceChanged,
+ x =>
+ {
+ if (x.HasResources)
+ {
+ ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
+ }
+
+ x.ResourcesChanged += SubResourceChanged;
+ },
+ x =>
+ {
+ if (x.HasResources)
+ {
+ ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
+ }
+
+ x.ResourcesChanged -= SubResourceChanged;
+ },
() => { });
}
///
public event EventHandler ResourcesChanged;
+ ///
+ public bool HasResources => _resources?.Count > 0 || this.Any(x => x.HasResources);
+
///
/// Gets or sets a dictionary of style resources.
///
diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
index 556e2324cd..8e571af4a0 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
@@ -50,6 +50,9 @@ namespace Avalonia.Markup.Xaml.Styling
}
}
+ ///
+ bool IResourceProvider.HasResources => Loaded.HasResources;
+
///
public void Attach(IStyleable control, IStyleHost container)
{
diff --git a/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs b/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs
index 4fa00ab171..561cad87c7 100644
--- a/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs
+++ b/tests/Avalonia.Controls.UnitTests/ControlTests_Resources.cs
@@ -2,6 +2,8 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System;
+using Avalonia.Controls.Presenters;
+using Avalonia.Controls.Templates;
using Avalonia.Styling;
using Avalonia.UnitTests;
using Xunit;
@@ -142,5 +144,112 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal("foo-value", target.FindResource("foo"));
}
+
+ [Fact]
+ public void Adding_Resource_Should_Call_Raise_ResourceChanged_On_Logical_Children()
+ {
+ Border child;
+
+ var target = new ContentControl
+ {
+ Content = child = new Border(),
+ Template = ContentControlTemplate(),
+ };
+
+ var raisedOnTarget = false;
+ var raisedOnPresenter = false;
+ var raisedOnChild = false;
+
+ target.Measure(Size.Infinity);
+ target.ResourcesChanged += (_, __) => raisedOnTarget = true;
+ target.Presenter.ResourcesChanged += (_, __) => raisedOnPresenter = true;
+ child.ResourcesChanged += (_, __) => raisedOnChild = true;
+
+ target.Resources.Add("foo", "bar");
+
+ Assert.True(raisedOnTarget);
+ Assert.False(raisedOnPresenter);
+ Assert.True(raisedOnChild);
+ }
+
+ [Fact]
+ public void Adding_Resource_To_Styles_Should_Raise_ResourceChanged()
+ {
+ var target = new Decorator();
+ var raised = false;
+
+ target.ResourcesChanged += (_, __) => raised = true;
+ target.Styles.Resources.Add("foo", "bar");
+
+ Assert.True(raised);
+ }
+
+ [Fact]
+ public void Adding_Resource_To_Nested_Style_Should_Raise_ResourceChanged()
+ {
+ Style style;
+ var target = new Decorator
+ {
+ Styles =
+ {
+ (style = new Style()),
+ }
+ };
+
+ var raised = false;
+
+ target.ResourcesChanged += (_, __) => raised = true;
+ style.Resources.Add("foo", "bar");
+
+ Assert.True(raised);
+ }
+
+ [Fact]
+ public void Adding_Style_With_Resource_Should_Raise_ResourceChanged()
+ {
+ Style style = new Style
+ {
+ Resources = { { "foo", "bar" } },
+ };
+
+ var target = new Decorator();
+ var raised = false;
+
+ target.ResourcesChanged += (_, __) => raised = true;
+ target.Styles.Add(style);
+
+ Assert.True(raised);
+ }
+
+ [Fact]
+ public void Removing_Style_With_Resource_Should_Raise_ResourceChanged()
+ {
+ var target = new Decorator
+ {
+ Styles =
+ {
+ new Style
+ {
+ Resources = { { "foo", "bar" } },
+ }
+ }
+ };
+ var raised = false;
+
+ target.ResourcesChanged += (_, __) => raised = true;
+ target.Styles.Clear();
+
+ Assert.True(raised);
+ }
+
+ private IControlTemplate ContentControlTemplate()
+ {
+ return new FuncControlTemplate(x =>
+ new ContentPresenter
+ {
+ Name = "PART_ContentPresenter",
+ [!ContentPresenter.ContentProperty] = x[!ContentControl.ContentProperty],
+ });
+ }
}
}