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], + }); + } } }