Skip to content

Commit

Permalink
Set resource parent for merged dictionaries.
Browse files Browse the repository at this point in the history
Allows `DynamicResource` and `StaticResource` in merged resource dictionaries to work properly.

Fixes AvaloniaUI#3323
  • Loading branch information
grokys committed Dec 5, 2019
1 parent c2f3f3f commit 601abac
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 49 deletions.
Original file line number Diff line number Diff line change
@@ -1,29 +1,27 @@
using Avalonia.Controls;

namespace Avalonia.Styling
namespace Avalonia.Controls
{
/// <summary>
/// Defines an interface through which a <see cref="Style"/>'s parent can be set.
/// Defines an interface through which an <see cref="IResourceNode"/>'s parent can be set.
/// </summary>
/// <remarks>
/// You should not usually need to use this interface - it is for internal use only.
/// </remarks>
public interface ISetStyleParent : IStyle
public interface ISetResourceParent : IResourceNode
{
/// <summary>
/// Sets the style parent.
/// Sets the resource parent.
/// </summary>
/// <param name="parent">The parent.</param>
void SetParent(IResourceNode parent);

/// <summary>
/// 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.
/// </summary>
/// <param name="e">The event args.</param>
/// <remarks>
/// This method will be called automatically by the framework, you should not need to call
/// this method yourself.
/// </remarks>
void NotifyResourcesChanged(ResourcesChangedEventArgs e);
void ParentResourcesChanged(ResourcesChangedEventArgs e);
}
}
62 changes: 60 additions & 2 deletions src/Avalonia.Styling/Controls/ResourceDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ namespace Avalonia.Controls
/// <summary>
/// An indexed dictionary of resources.
/// </summary>
public class ResourceDictionary : AvaloniaDictionary<object, object>, IResourceDictionary
public class ResourceDictionary : AvaloniaDictionary<object, object>,
IResourceDictionary,
IResourceNode,
ISetResourceParent
{
private IResourceNode _parent;
private AvaloniaList<IResourceProvider> _mergedDictionaries;

/// <summary>
Expand All @@ -39,6 +43,12 @@ public IList<IResourceProvider> MergedDictionaries
_mergedDictionaries.ForEachItem(
x =>
{
if (x is ISetResourceParent setParent)
{
setParent.SetParent(this);
setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
}
if (x.HasResources)
{
OnResourcesChanged();
Expand All @@ -48,11 +58,18 @@ public IList<IResourceProvider> MergedDictionaries
},
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;
},
() => { });
Expand All @@ -68,6 +85,27 @@ bool IResourceProvider.HasResources
get => Count > 0 || (_mergedDictionaries?.Any(x => x.HasResources) ?? false);
}

/// <inheritdoc/>
IResourceNode IResourceNode.ResourceParent => _parent;

/// <inheritdoc/>
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
NotifyMergedDictionariesResourcesChanged(e);
ResourcesChanged?.Invoke(this, e);
}

/// <inheritdoc/>
void ISetResourceParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
throw new InvalidOperationException("The ResourceDictionary already has a parent.");
}

_parent = parent;
}

/// <inheritdoc/>
public bool TryGetResource(object key, out object value)
{
Expand Down Expand Up @@ -95,7 +133,27 @@ private void OnResourcesChanged()
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();
}
}
4 changes: 2 additions & 2 deletions src/Avalonia.Styling/StyledElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,13 @@ public Styles Styles
{
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);
}
Expand Down
12 changes: 6 additions & 6 deletions src/Avalonia.Styling/Styling/Style.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Avalonia.Styling
/// <summary>
/// Defines a style.
/// </summary>
public class Style : AvaloniaObject, IStyle, ISetStyleParent
public class Style : AvaloniaObject, IStyle, ISetResourceParent
{
private static Dictionary<IStyleable, CompositeDisposable> _applied =
new Dictionary<IStyleable, CompositeDisposable>();
Expand Down Expand Up @@ -59,16 +59,16 @@ public IResourceDictionary Resources

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());
}
}
}
Expand Down Expand Up @@ -194,13 +194,13 @@ public override string ToString()
}

/// <inheritdoc/>
void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}

/// <inheritdoc/>
void ISetStyleParent.SetParent(IResourceNode parent)
void ISetResourceParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
Expand Down
20 changes: 10 additions & 10 deletions src/Avalonia.Styling/Styling/Styles.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Avalonia.Styling
/// <summary>
/// A style that consists of a number of child styles.
/// </summary>
public class Styles : AvaloniaObject, IAvaloniaList<IStyle>, IStyle, ISetStyleParent
public class Styles : AvaloniaObject, IAvaloniaList<IStyle>, IStyle, ISetResourceParent
{
private IResourceNode _parent;
private IResourceDictionary _resources;
Expand All @@ -27,10 +27,10 @@ public Styles()
_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)
Expand All @@ -43,10 +43,10 @@ public Styles()
},
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)
Expand Down Expand Up @@ -98,7 +98,7 @@ public IResourceDictionary Resources

if (hadResources || _resources.Count > 0)
{
((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
((ISetResourceParent)this).ParentResourcesChanged(new ResourcesChangedEventArgs());
}
}
}
Expand Down Expand Up @@ -246,7 +246,7 @@ public bool TryGetResource(object key, out object value)
IEnumerator IEnumerable.GetEnumerator() => _styles.GetEnumerator();

/// <inheritdoc/>
void ISetStyleParent.SetParent(IResourceNode parent)
void ISetResourceParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
Expand All @@ -257,7 +257,7 @@ void ISetStyleParent.SetParent(IResourceNode parent)
}

/// <inheritdoc/>
void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
ResourcesChanged?.Invoke(this, e);
}
Expand All @@ -266,7 +266,7 @@ private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs
{
foreach (var child in this)
{
(child as ISetStyleParent)?.NotifyResourcesChanged(e);
(child as ISetResourceParent)?.ParentResourcesChanged(e);
}

ResourcesChanged?.Invoke(this, e);
Expand All @@ -280,7 +280,7 @@ private void SubResourceChanged(object sender, ResourcesChangedEventArgs e)
{
if (foundSource)
{
(child as ISetStyleParent)?.NotifyResourcesChanged(e);
(child as ISetResourceParent)?.ParentResourcesChanged(e);
}

foundSource |= child == sender;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
/// <summary>
/// Loads a resource dictionary from a specified URL.
/// </summary>
public class ResourceInclude :IResourceProvider
public class ResourceInclude : IResourceNode, ISetResourceParent
{
private IResourceNode _parent;
private Uri _baseUri;
private IResourceDictionary _loaded;

Expand All @@ -26,6 +27,9 @@ public IResourceDictionary Loaded
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());
Expand All @@ -44,12 +48,32 @@ public IResourceDictionary Loaded
/// <inhertidoc/>
bool IResourceProvider.HasResources => Loaded.HasResources;

/// <inhertidoc/>
IResourceNode IResourceNode.ResourceParent => _parent;

/// <inhertidoc/>
bool IResourceProvider.TryGetResource(object key, out object value)
{
return Loaded.TryGetResource(key, out value);
}

/// <inhertidoc/>
void ISetResourceParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
throw new InvalidOperationException("The ResourceInclude already has a parent.");
}

_parent = parent;
}

/// <inhertidoc/>
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
(_loaded as ISetResourceParent)?.ParentResourcesChanged(e);
}

public ResourceInclude ProvideValue(IServiceProvider serviceProvider)
{
var tdc = (ITypeDescriptorContext)serviceProvider;
Expand Down
10 changes: 5 additions & 5 deletions src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Styling
/// <summary>
/// Includes a style from a URL.
/// </summary>
public class StyleInclude : IStyle, ISetStyleParent
public class StyleInclude : IStyle, ISetResourceParent
{
private Uri _baseUri;
private IStyle _loaded;
Expand Down Expand Up @@ -53,7 +53,7 @@ public IStyle Loaded
{
var loader = new AvaloniaXamlLoader();
_loaded = (IStyle)loader.Load(Source, _baseUri);
(_loaded as ISetStyleParent)?.SetParent(this);
(_loaded as ISetResourceParent)?.SetParent(this);
}

return _loaded;
Expand Down Expand Up @@ -89,13 +89,13 @@ public void Detach()
public bool TryGetResource(object key, out object value) => Loaded.TryGetResource(key, out value);

/// <inheritdoc/>
void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
{
(Loaded as ISetStyleParent)?.NotifyResourcesChanged(e);
(Loaded as ISetResourceParent)?.ParentResourcesChanged(e);
}

/// <inheritdoc/>
void ISetStyleParent.SetParent(IResourceNode parent)
void ISetResourceParent.SetParent(IResourceNode parent)
{
if (_parent != null && parent != null)
{
Expand Down
Loading

0 comments on commit 601abac

Please sign in to comment.