Skip to content

Commit fe2d2ac

Browse files
authored
Merge pull request AvaloniaUI#3327 from AvaloniaUI/fixes/3323-resourcedictionary-resource
Fix referencing resources in merged dictionaries
2 parents e48bbe6 + e41c586 commit fe2d2ac

File tree

9 files changed

+267
-49
lines changed

9 files changed

+267
-49
lines changed
Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
1-
using Avalonia.Controls;
2-
3-
namespace Avalonia.Styling
1+
namespace Avalonia.Controls
42
{
53
/// <summary>
6-
/// Defines an interface through which a <see cref="Style"/>'s parent can be set.
4+
/// Defines an interface through which an <see cref="IResourceNode"/>'s parent can be set.
75
/// </summary>
86
/// <remarks>
97
/// You should not usually need to use this interface - it is for internal use only.
108
/// </remarks>
11-
public interface ISetStyleParent : IStyle
9+
public interface ISetResourceParent : IResourceNode
1210
{
1311
/// <summary>
14-
/// Sets the style parent.
12+
/// Sets the resource parent.
1513
/// </summary>
1614
/// <param name="parent">The parent.</param>
1715
void SetParent(IResourceNode parent);
1816

1917
/// <summary>
20-
/// Notifies the style that a change has been made to resources that apply to it.
18+
/// Notifies the resource node that a change has been made to the resources in its parent.
2119
/// </summary>
2220
/// <param name="e">The event args.</param>
2321
/// <remarks>
2422
/// This method will be called automatically by the framework, you should not need to call
2523
/// this method yourself.
2624
/// </remarks>
27-
void NotifyResourcesChanged(ResourcesChangedEventArgs e);
25+
void ParentResourcesChanged(ResourcesChangedEventArgs e);
2826
}
2927
}

src/Avalonia.Styling/Controls/ResourceDictionary.cs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ namespace Avalonia.Controls
1212
/// <summary>
1313
/// An indexed dictionary of resources.
1414
/// </summary>
15-
public class ResourceDictionary : AvaloniaDictionary<object, object>, IResourceDictionary
15+
public class ResourceDictionary : AvaloniaDictionary<object, object>,
16+
IResourceDictionary,
17+
IResourceNode,
18+
ISetResourceParent
1619
{
20+
private IResourceNode _parent;
1721
private AvaloniaList<IResourceProvider> _mergedDictionaries;
1822

1923
/// <summary>
@@ -39,6 +43,12 @@ public IList<IResourceProvider> MergedDictionaries
3943
_mergedDictionaries.ForEachItem(
4044
x =>
4145
{
46+
if (x is ISetResourceParent setParent)
47+
{
48+
setParent.SetParent(this);
49+
setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
50+
}
51+
4252
if (x.HasResources)
4353
{
4454
OnResourcesChanged();
@@ -48,11 +58,18 @@ public IList<IResourceProvider> MergedDictionaries
4858
},
4959
x =>
5060
{
61+
if (x is ISetResourceParent setParent)
62+
{
63+
setParent.SetParent(null);
64+
setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
65+
}
66+
5167
if (x.HasResources)
5268
{
5369
OnResourcesChanged();
5470
}
5571

72+
(x as ISetResourceParent)?.SetParent(null);
5673
x.ResourcesChanged -= MergedDictionaryResourcesChanged;
5774
},
5875
() => { });
@@ -68,6 +85,27 @@ bool IResourceProvider.HasResources
6885
get => Count > 0 || (_mergedDictionaries?.Any(x => x.HasResources) ?? false);
6986
}
7087

88+
/// <inheritdoc/>
89+
IResourceNode IResourceNode.ResourceParent => _parent;
90+
91+
/// <inheritdoc/>
92+
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
93+
{
94+
NotifyMergedDictionariesResourcesChanged(e);
95+
ResourcesChanged?.Invoke(this, e);
96+
}
97+
98+
/// <inheritdoc/>
99+
void ISetResourceParent.SetParent(IResourceNode parent)
100+
{
101+
if (_parent != null && parent != null)
102+
{
103+
throw new InvalidOperationException("The ResourceDictionary already has a parent.");
104+
}
105+
106+
_parent = parent;
107+
}
108+
71109
/// <inheritdoc/>
72110
public bool TryGetResource(object key, out object value)
73111
{
@@ -95,7 +133,27 @@ private void OnResourcesChanged()
95133
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
96134
}
97135

98-
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) => OnResourcesChanged();
136+
private void NotifyMergedDictionariesResourcesChanged(ResourcesChangedEventArgs e)
137+
{
138+
if (_mergedDictionaries != null)
139+
{
140+
for (var i = _mergedDictionaries.Count - 1; i >= 0; --i)
141+
{
142+
if (_mergedDictionaries[i] is ISetResourceParent merged)
143+
{
144+
merged.ParentResourcesChanged(e);
145+
}
146+
}
147+
}
148+
}
149+
150+
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
151+
{
152+
var ev = new ResourcesChangedEventArgs();
153+
NotifyMergedDictionariesResourcesChanged(ev);
154+
OnResourcesChanged();
155+
}
156+
99157
private void MergedDictionaryResourcesChanged(object sender, ResourcesChangedEventArgs e) => OnResourcesChanged();
100158
}
101159
}

src/Avalonia.Styling/StyledElement.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,13 +223,13 @@ public Styles Styles
223223
{
224224
if (_styles != null)
225225
{
226-
(_styles as ISetStyleParent)?.SetParent(null);
226+
(_styles as ISetResourceParent)?.SetParent(null);
227227
_styles.ResourcesChanged -= ThisResourcesChanged;
228228
}
229229

230230
_styles = value;
231231

232-
if (value is ISetStyleParent setParent && setParent.ResourceParent == null)
232+
if (value is ISetResourceParent setParent && setParent.ResourceParent == null)
233233
{
234234
setParent.SetParent(this);
235235
}

src/Avalonia.Styling/Styling/Style.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Avalonia.Styling
1414
/// <summary>
1515
/// Defines a style.
1616
/// </summary>
17-
public class Style : AvaloniaObject, IStyle, ISetStyleParent
17+
public class Style : AvaloniaObject, IStyle, ISetResourceParent
1818
{
1919
private static Dictionary<IStyleable, CompositeDisposable> _applied =
2020
new Dictionary<IStyleable, CompositeDisposable>();
@@ -59,16 +59,16 @@ public IResourceDictionary Resources
5959

6060
if (_resources != null)
6161
{
62-
hadResources = _resources.Count > 0;
62+
hadResources = _resources.HasResources;
6363
_resources.ResourcesChanged -= ResourceDictionaryChanged;
6464
}
6565

6666
_resources = value;
6767
_resources.ResourcesChanged += ResourceDictionaryChanged;
6868

69-
if (hadResources || _resources.Count > 0)
69+
if (hadResources || _resources.HasResources)
7070
{
71-
((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
71+
((ISetResourceParent)this).ParentResourcesChanged(new ResourcesChangedEventArgs());
7272
}
7373
}
7474
}
@@ -194,13 +194,13 @@ public override string ToString()
194194
}
195195

196196
/// <inheritdoc/>
197-
void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
197+
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
198198
{
199199
ResourcesChanged?.Invoke(this, e);
200200
}
201201

202202
/// <inheritdoc/>
203-
void ISetStyleParent.SetParent(IResourceNode parent)
203+
void ISetResourceParent.SetParent(IResourceNode parent)
204204
{
205205
if (_parent != null && parent != null)
206206
{

src/Avalonia.Styling/Styling/Styles.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ namespace Avalonia.Styling
1414
/// <summary>
1515
/// A style that consists of a number of child styles.
1616
/// </summary>
17-
public class Styles : AvaloniaObject, IAvaloniaList<IStyle>, IStyle, ISetStyleParent
17+
public class Styles : AvaloniaObject, IAvaloniaList<IStyle>, IStyle, ISetResourceParent
1818
{
1919
private IResourceNode _parent;
2020
private IResourceDictionary _resources;
@@ -27,10 +27,10 @@ public Styles()
2727
_styles.ForEachItem(
2828
x =>
2929
{
30-
if (x.ResourceParent == null && x is ISetStyleParent setParent)
30+
if (x.ResourceParent == null && x is ISetResourceParent setParent)
3131
{
3232
setParent.SetParent(this);
33-
setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs());
33+
setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
3434
}
3535

3636
if (x.HasResources)
@@ -43,10 +43,10 @@ public Styles()
4343
},
4444
x =>
4545
{
46-
if (x.ResourceParent == this && x is ISetStyleParent setParent)
46+
if (x.ResourceParent == this && x is ISetResourceParent setParent)
4747
{
4848
setParent.SetParent(null);
49-
setParent.NotifyResourcesChanged(new ResourcesChangedEventArgs());
49+
setParent.ParentResourcesChanged(new ResourcesChangedEventArgs());
5050
}
5151

5252
if (x.HasResources)
@@ -98,7 +98,7 @@ public IResourceDictionary Resources
9898

9999
if (hadResources || _resources.Count > 0)
100100
{
101-
((ISetStyleParent)this).NotifyResourcesChanged(new ResourcesChangedEventArgs());
101+
((ISetResourceParent)this).ParentResourcesChanged(new ResourcesChangedEventArgs());
102102
}
103103
}
104104
}
@@ -246,7 +246,7 @@ public bool TryGetResource(object key, out object value)
246246
IEnumerator IEnumerable.GetEnumerator() => _styles.GetEnumerator();
247247

248248
/// <inheritdoc/>
249-
void ISetStyleParent.SetParent(IResourceNode parent)
249+
void ISetResourceParent.SetParent(IResourceNode parent)
250250
{
251251
if (_parent != null && parent != null)
252252
{
@@ -257,7 +257,7 @@ void ISetStyleParent.SetParent(IResourceNode parent)
257257
}
258258

259259
/// <inheritdoc/>
260-
void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
260+
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
261261
{
262262
ResourcesChanged?.Invoke(this, e);
263263
}
@@ -266,7 +266,7 @@ private void ResourceDictionaryChanged(object sender, ResourcesChangedEventArgs
266266
{
267267
foreach (var child in this)
268268
{
269-
(child as ISetStyleParent)?.NotifyResourcesChanged(e);
269+
(child as ISetResourceParent)?.ParentResourcesChanged(e);
270270
}
271271

272272
ResourcesChanged?.Invoke(this, e);
@@ -280,7 +280,7 @@ private void SubResourceChanged(object sender, ResourcesChangedEventArgs e)
280280
{
281281
if (foundSource)
282282
{
283-
(child as ISetStyleParent)?.NotifyResourcesChanged(e);
283+
(child as ISetResourceParent)?.ParentResourcesChanged(e);
284284
}
285285

286286
foundSource |= child == sender;

src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
77
/// <summary>
88
/// Loads a resource dictionary from a specified URL.
99
/// </summary>
10-
public class ResourceInclude :IResourceProvider
10+
public class ResourceInclude : IResourceNode, ISetResourceParent
1111
{
12+
private IResourceNode _parent;
1213
private Uri _baseUri;
1314
private IResourceDictionary _loaded;
1415

@@ -26,6 +27,9 @@ public IResourceDictionary Loaded
2627
var loader = new AvaloniaXamlLoader();
2728
_loaded = (IResourceDictionary)loader.Load(Source, _baseUri);
2829

30+
(_loaded as ISetResourceParent)?.SetParent(this);
31+
_loaded.ResourcesChanged += ResourcesChanged;
32+
2933
if (_loaded.HasResources)
3034
{
3135
ResourcesChanged?.Invoke(this, new ResourcesChangedEventArgs());
@@ -44,12 +48,32 @@ public IResourceDictionary Loaded
4448
/// <inhertidoc/>
4549
bool IResourceProvider.HasResources => Loaded.HasResources;
4650

51+
/// <inhertidoc/>
52+
IResourceNode IResourceNode.ResourceParent => _parent;
53+
4754
/// <inhertidoc/>
4855
bool IResourceProvider.TryGetResource(object key, out object value)
4956
{
5057
return Loaded.TryGetResource(key, out value);
5158
}
5259

60+
/// <inhertidoc/>
61+
void ISetResourceParent.SetParent(IResourceNode parent)
62+
{
63+
if (_parent != null && parent != null)
64+
{
65+
throw new InvalidOperationException("The ResourceInclude already has a parent.");
66+
}
67+
68+
_parent = parent;
69+
}
70+
71+
/// <inhertidoc/>
72+
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
73+
{
74+
(_loaded as ISetResourceParent)?.ParentResourcesChanged(e);
75+
}
76+
5377
public ResourceInclude ProvideValue(IServiceProvider serviceProvider)
5478
{
5579
var tdc = (ITypeDescriptorContext)serviceProvider;

src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Avalonia.Markup.Xaml.Styling
1010
/// <summary>
1111
/// Includes a style from a URL.
1212
/// </summary>
13-
public class StyleInclude : IStyle, ISetStyleParent
13+
public class StyleInclude : IStyle, ISetResourceParent
1414
{
1515
private Uri _baseUri;
1616
private IStyle _loaded;
@@ -53,7 +53,7 @@ public IStyle Loaded
5353
{
5454
var loader = new AvaloniaXamlLoader();
5555
_loaded = (IStyle)loader.Load(Source, _baseUri);
56-
(_loaded as ISetStyleParent)?.SetParent(this);
56+
(_loaded as ISetResourceParent)?.SetParent(this);
5757
}
5858

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

9191
/// <inheritdoc/>
92-
void ISetStyleParent.NotifyResourcesChanged(ResourcesChangedEventArgs e)
92+
void ISetResourceParent.ParentResourcesChanged(ResourcesChangedEventArgs e)
9393
{
94-
(Loaded as ISetStyleParent)?.NotifyResourcesChanged(e);
94+
(Loaded as ISetResourceParent)?.ParentResourcesChanged(e);
9595
}
9696

9797
/// <inheritdoc/>
98-
void ISetStyleParent.SetParent(IResourceNode parent)
98+
void ISetResourceParent.SetParent(IResourceNode parent)
9999
{
100100
if (_parent != null && parent != null)
101101
{

0 commit comments

Comments
 (0)