diff --git a/src/Avalonia.Styling/Styling/ISetStyleParent.cs b/src/Avalonia.Styling/Controls/ISetResourceParent.cs
similarity index 61%
rename from src/Avalonia.Styling/Styling/ISetStyleParent.cs
rename to src/Avalonia.Styling/Controls/ISetResourceParent.cs
index bca3d9d714..a1264adc34 100644
--- a/src/Avalonia.Styling/Styling/ISetStyleParent.cs
+++ b/src/Avalonia.Styling/Controls/ISetResourceParent.cs
@@ -1,29 +1,27 @@
-using Avalonia.Controls;
-
-namespace Avalonia.Styling
+namespace Avalonia.Controls
{
///
- /// Defines an interface through which a 's parent can be set.
+ /// Defines an interface through which an 's parent can be set.
///
///
/// You should not usually need to use this interface - it is for internal use only.
///
- public interface ISetStyleParent : IStyle
+ public interface ISetResourceParent : IResourceNode
{
///
- /// Sets the style parent.
+ /// Sets the resource parent.
///
/// The parent.
void SetParent(IResourceNode parent);
///
- /// Notifies the style that a change has been made to resources that apply to it.
+ /// Notifies the resource node that a change has been made to the resources in its parent.
///
/// The event args.
///
/// This method will be called automatically by the framework, you should not need to call
/// this method yourself.
///
- void NotifyResourcesChanged(ResourcesChangedEventArgs e);
+ void ParentResourcesChanged(ResourcesChangedEventArgs e);
}
}
diff --git a/src/Avalonia.Styling/Controls/ResourceDictionary.cs b/src/Avalonia.Styling/Controls/ResourceDictionary.cs
index 901e27b7b7..acc2db1ff7 100644
--- a/src/Avalonia.Styling/Controls/ResourceDictionary.cs
+++ b/src/Avalonia.Styling/Controls/ResourceDictionary.cs
@@ -12,8 +12,12 @@ namespace Avalonia.Controls
///
/// An indexed dictionary of resources.
///
- public class ResourceDictionary : AvaloniaDictionary, IResourceDictionary
+ public class ResourceDictionary : AvaloniaDictionary,
+ IResourceDictionary,
+ IResourceNode,
+ ISetResourceParent
{
+ private IResourceNode _parent;
private AvaloniaList _mergedDictionaries;
///
@@ -39,6 +43,12 @@ namespace Avalonia.Controls
_mergedDictionaries.ForEachItem(
x =>
{
+ if (x is ISetResourceParent setParent)
+ {
+ setParent.SetParent(this);
+ setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
+ }
+
if (x.HasResources)
{
OnResourcesChanged();
@@ -48,11 +58,18 @@ namespace Avalonia.Controls
},
x =>
{
+ if (x is ISetResourceParent setParent)
+ {
+ setParent.SetParent(null);
+ setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
+ }
+
if (x.HasResources)
{
OnResourcesChanged();
}
+ (x as ISetResourceParent)?.SetParent(null);
x.ResourcesChanged -= MergedDictionaryResourcesChanged;
},
() => { });
@@ -68,6 +85,27 @@ namespace Avalonia.Controls
get => Count > 0 || (_mergedDictionaries?.Any(x => x.HasResources) ?? false);
}
+ ///
+ IResourceNode IResourceNode.ResourceParent => _parent;
+
+ ///
+ void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
+ {
+ NotifyMergedDictionariesResourcesChanged(e);
+ ResourcesChanged?.Invoke(this, e);
+ }
+
+ ///
+ void ISetResourceParent.SetParent(IResourceNode parent)
+ {
+ if (_parent != null && parent != null)
+ {
+ throw new InvalidOperationException("The ResourceDictionary already has a parent.");
+ }
+
+ _parent = parent;
+ }
+
///
public bool TryGetResource(object key, out object value)
{
@@ -95,7 +133,27 @@ namespace Avalonia.Controls
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
}
- private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => OnResourcesChanged();
+ private void NotifyMergedDictionariesResourcesChanged(ResourcesChangedEventArgs e)
+ {
+ if (_mergedDictionaries != null)
+ {
+ for (var i = _mergedDictionaries.Count - 1; i >= 0; --i)
+ {
+ if (_mergedDictionaries[i] is ISetResourceParent merged)
+ {
+ merged.ParentResourcesChanged(e);
+ }
+ }
+ }
+ }
+
+ private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+ {
+ var ev = new ResourcesChangedEventArgs();
+ NotifyMergedDictionariesResourcesChanged(ev);
+ OnResourcesChanged();
+ }
+
private void MergedDictionaryResourcesChanged(object sender, ResourcesChangedEventArgs e) => OnResourcesChanged();
}
}
diff --git a/src/Avalonia.Styling/StyledElement.cs b/src/Avalonia.Styling/StyledElement.cs
index 5e1bcde2f6..b5deb9a4a1 100644
--- a/src/Avalonia.Styling/StyledElement.cs
+++ b/src/Avalonia.Styling/StyledElement.cs
@@ -223,13 +223,13 @@ namespace Avalonia
{
if (_styles != null)
{
- (_styles as ISetStyleParent)?.SetParent(null);
+ (_styles as ISetResourceParent)?.SetParent(null);
_styles.ResourcesChanged -= ThisResourcesChanged;
}
_styles = value;
- if (value is ISetStyleParent setParent && setParent.ResourceParent == null)
+ if (value is ISetResourceParent setParent && setParent.ResourceParent == null)
{
setParent.SetParent(this);
}
diff --git a/src/Avalonia.Styling/Styling/Style.cs b/src/Avalonia.Styling/Styling/Style.cs
index 3ce82b4160..99ee8d8563 100644
--- a/src/Avalonia.Styling/Styling/Style.cs
+++ b/src/Avalonia.Styling/Styling/Style.cs
@@ -14,7 +14,7 @@ namespace Avalonia.Styling
///
/// Defines a style.
///
- public class Style : AvaloniaObject, IStyle, ISetStyleParent
+ public class Style : AvaloniaObject, IStyle, ISetResourceParent
{
private static Dictionary _applied =
new Dictionary();
@@ -59,16 +59,16 @@ namespace Avalonia.Styling
if (_resources != null)
{
- hadResources = _resources.Count > 0;
+ hadResources = _resources.HasResources;
_resources.ResourcesChanged -= ResourceDictionaryChanged;
}
_resources = value;
_resources.ResourcesChanged += ResourceDictionaryChanged;
- if (hadResources || _resources.Count > 0)
+ if (hadResources || _resources.HasResources)
{
- ((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
+ ((ISetResourceParent)this).ParentResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
@@ -194,13 +194,13 @@ namespace Avalonia.Styling
}
///
- void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
+ void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
///
- void ISetStyleParent.SetParent(IResourceNode parent)
+ void ISetResourceParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
diff --git a/src/Avalonia.Styling/Styling/Styles.cs b/src/Avalonia.Styling/Styling/Styles.cs
index a4563110a9..0226288998 100644
--- a/src/Avalonia.Styling/Styling/Styles.cs
+++ b/src/Avalonia.Styling/Styling/Styles.cs
@@ -14,7 +14,7 @@ namespace Avalonia.Styling
///
/// A style that consists of a number of child styles.
///
- public class Styles : AvaloniaObject, IAvaloniaList, IStyle, ISetStyleParent
+ public class Styles : AvaloniaObject, IAvaloniaList, IStyle, ISetResourceParent
{
private IResourceNode _parent;
private IResourceDictionary _resources;
@@ -27,10 +27,10 @@ namespace Avalonia.Styling
_styles.ForEachItem(
x =>
{
- if (x.ResourceParent == null && x is ISetStyleParent setParent)
+ if (x.ResourceParent == null && x is ISetResourceParent setParent)
{
setParent.SetParent(this);
- setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs());
+ setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
}
if (x.HasResources)
@@ -43,10 +43,10 @@ namespace Avalonia.Styling
},
x =>
{
- if (x.ResourceParent == this && x is ISetStyleParent setParent)
+ if (x.ResourceParent == this && x is ISetResourceParent setParent)
{
setParent.SetParent(null);
- setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs());
+ setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
}
if (x.HasResources)
@@ -98,7 +98,7 @@ namespace Avalonia.Styling
if (hadResources || _resources.Count > 0)
{
- ((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
+ ((ISetResourceParent)this).ParentResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
@@ -246,7 +246,7 @@ namespace Avalonia.Styling
IEnumerator IEnumerable.GetEnumerator() => _styles.GetEnumerator();
///
- void ISetStyleParent.SetParent(IResourceNode parent)
+ void ISetResourceParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
@@ -257,7 +257,7 @@ namespace Avalonia.Styling
}
///
- void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
+ void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
@@ -266,7 +266,7 @@ namespace Avalonia.Styling
{
foreach (var child in this)
{
- (child as ISetStyleParent)?.NotifyResourcesChanged(e);
+ (child as ISetResourceParent)?.ParentResourcesChanged(e);
}
ResourcesChanged?.Invoke(this, e);
@@ -280,7 +280,7 @@ namespace Avalonia.Styling
{
if (foundSource)
{
- (child as ISetStyleParent)?.NotifyResourcesChanged(e);
+ (child as ISetResourceParent)?.ParentResourcesChanged(e);
}
foundSource |= child == sender;
diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
index 3525628a79..0d56942645 100644
--- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs
@@ -7,8 +7,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
///
/// Loads a resource dictionary from a specified URL.
///
- public class ResourceInclude :IResourceProvider
+ public class ResourceInclude : IResourceNode, ISetResourceParent
{
+ private IResourceNode _parent;
private Uri _baseUri;
private IResourceDictionary _loaded;
@@ -26,6 +27,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
var loader = new AvaloniaXamlLoader();
_loaded = (IResourceDictionary)loader.Load(Source, _baseUri);
+ (_loaded as ISetResourceParent)?.SetParent(this);
+ _loaded.ResourcesChanged += ResourcesChanged;
+
if (_loaded.HasResources)
{
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
@@ -44,12 +48,32 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
///
bool IResourceProvider.HasResources => Loaded.HasResources;
+ ///
+ IResourceNode IResourceNode.ResourceParent => _parent;
+
///
bool IResourceProvider.TryGetResource(object key, out object value)
{
return Loaded.TryGetResource(key, out value);
}
+ ///
+ void ISetResourceParent.SetParent(IResourceNode parent)
+ {
+ if (_parent != null && parent != null)
+ {
+ throw new InvalidOperationException("The ResourceInclude already has a parent.");
+ }
+
+ _parent = parent;
+ }
+
+ ///
+ void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
+ {
+ (_loaded as ISetResourceParent)?.ParentResourcesChanged(e);
+ }
+
public ResourceInclude ProvideValue(IServiceProvider serviceProvider)
{
var tdc = (ITypeDescriptorContext)serviceProvider;
diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
index 7acee50d80..41eab79ed8 100644
--- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
+++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
@@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Styling
///
/// Includes a style from a URL.
///
- public class StyleInclude : IStyle, ISetStyleParent
+ public class StyleInclude : IStyle, ISetResourceParent
{
private Uri _baseUri;
private IStyle _loaded;
@@ -53,7 +53,7 @@ namespace Avalonia.Markup.Xaml.Styling
{
var loader = new AvaloniaXamlLoader();
_loaded = (IStyle)loader.Load(Source, _baseUri);
- (_loaded as ISetStyleParent)?.SetParent(this);
+ (_loaded as ISetResourceParent)?.SetParent(this);
}
return _loaded;
@@ -89,13 +89,13 @@ namespace Avalonia.Markup.Xaml.Styling
public bool TryGetResource(object key, out object value) => Loaded.TryGetResource(key, out value);
///
- void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
+ void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
- (Loaded as ISetStyleParent)?.NotifyResourcesChanged(e);
+ (Loaded as ISetResourceParent)?.ParentResourcesChanged(e);
}
///
- void ISetStyleParent.SetParent(IResourceNode parent)
+ void ISetResourceParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
diff --git a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs
index 1eb3cd9750..56316b0cea 100644
--- a/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs
+++ b/tests/Avalonia.Styling.UnitTests/ResourceDictionaryTests.cs
@@ -3,6 +3,7 @@
using System;
using Avalonia.Controls;
+using Moq;
using Xunit;
namespace Avalonia.Styling.UnitTests
@@ -136,7 +137,7 @@ namespace Avalonia.Styling.UnitTests
}
[Fact]
- public void ResourcesChanged_Should_Not_Be_Raised_On_Empty_MergedDictionary_Remove()
+ public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Resource_Add()
{
var target = new ResourceDictionary
{
@@ -145,31 +146,45 @@ namespace Avalonia.Styling.UnitTests
new ResourceDictionary(),
}
};
+
var raised = false;
target.ResourcesChanged += (_, __) => raised = true;
- target.MergedDictionaries.RemoveAt(0);
+ ((IResourceDictionary)target.MergedDictionaries[0]).Add("foo", "bar");
- Assert.False(raised);
+ Assert.True(raised);
}
[Fact]
- public void ResourcesChanged_Should_Be_Raised_On_MergedDictionary_Resource_Add()
+ public void MergedDictionary_ParentResourcesChanged_Should_Be_Called_On_Resource_Add()
{
- var target = new ResourceDictionary
- {
- MergedDictionaries =
- {
- new ResourceDictionary(),
- }
- };
+ var target = new ResourceDictionary();
+ var merged = new Mock();
- var raised = false;
+ target.MergedDictionaries.Add(merged.Object);
+ merged.ResetCalls();
- target.ResourcesChanged += (_, __) => raised = true;
- ((IResourceDictionary)target.MergedDictionaries[0]).Add("foo", "bar");
+ target.Add("foo", "bar");
- Assert.True(raised);
+ merged.Verify(
+ x => x.ParentResourcesChanged(It.IsAny()),
+ Times.Once);
+ }
+
+ [Fact]
+ public void MergedDictionary_ParentResourcesChanged_Should_Be_Called_On_NotifyResourceChanged()
+ {
+ var target = new ResourceDictionary();
+ var merged = new Mock();
+
+ target.MergedDictionaries.Add(merged.Object);
+ merged.ResetCalls();
+
+ ((ISetResourceParent)target).ParentResourcesChanged(new ResourcesChangedEventArgs());
+
+ merged.Verify(
+ x => x.ParentResourcesChanged(It.IsAny()),
+ Times.Once);
}
}
}