Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix referencing resources in merged dictionaries #3327

Merged
merged 4 commits into from
Dec 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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