-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
/
Copy pathStaticResourceExtension.cs
138 lines (118 loc) · 4.9 KB
/
StaticResourceExtension.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using System;
using System.Collections.Generic;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Markup.Data;
using Avalonia.Markup.Xaml.Converters;
using Avalonia.Markup.Xaml.XamlIl.Runtime;
using Avalonia.Styling;
namespace Avalonia.Markup.Xaml.MarkupExtensions
{
public class StaticResourceExtension
{
public StaticResourceExtension()
{
}
public StaticResourceExtension(object resourceKey)
{
ResourceKey = resourceKey;
}
public object? ResourceKey { get; set; }
public object? ProvideValue(IServiceProvider serviceProvider)
{
if (ResourceKey is not { } resourceKey)
{
throw new ArgumentException("StaticResourceExtension.ResourceKey must be set.");
}
var stack = serviceProvider.GetService<IAvaloniaXamlIlParentStackProvider>();
var provideTarget = serviceProvider.GetService<IProvideValueTarget>();
var targetObject = provideTarget?.TargetObject;
var targetProperty = provideTarget?.TargetProperty switch
{
AvaloniaProperty ap => ap,
PropertyInfo pi => new Avalonia.Data.Core.ReflectionClrPropertyInfo(pi),
_ => provideTarget?.TargetProperty,
};
var themeVariant = (targetObject as IThemeVariantHost)?.ActualThemeVariant
?? GetDictionaryVariant(stack);
var targetType = targetProperty switch
{
AvaloniaProperty ap => ap.PropertyType,
Avalonia.Data.Core.IPropertyInfo cpi => cpi.PropertyType,
_ => null
};
if (targetObject is Setter { Property: { } setterProperty })
{
targetType = setterProperty.PropertyType;
}
// Look upwards though the ambient context for IResourceNodes
// which might be able to give us the resource.
if (stack is not null)
{
// avoid allocations iterating the parents when possible
if (stack is IAvaloniaXamlIlEagerParentStackProvider eagerStack)
{
var enumerator = new EagerParentStackEnumerator(eagerStack);
while (enumerator.TryGetNextOfType<IResourceNode>() is { } node)
{
if (node.TryGetResource(resourceKey, themeVariant, out var value))
{
return ColorToBrushConverter.Convert(value, targetType);
}
}
}
else
{
foreach (var parent in stack.Parents)
{
if (parent is IResourceNode node && node.TryGetResource(resourceKey, themeVariant, out var value))
{
return ColorToBrushConverter.Convert(value, targetType);
}
}
}
}
if (targetObject is Control target &&
targetProperty is Avalonia.Data.Core.IPropertyInfo property)
{
// This is stored locally to avoid allocating closure in the outer scope.
var localTargetType = targetType;
var localInstance = this;
DelayedBinding.Add(target, property, x => localInstance.GetValue(x, localTargetType));
return AvaloniaProperty.UnsetValue;
}
throw new KeyNotFoundException($"Static resource '{resourceKey}' not found.");
}
private object? GetValue(StyledElement control, Type? targetType)
{
return ColorToBrushConverter.Convert(control.FindResource(ResourceKey!), targetType);
}
internal static ThemeVariant? GetDictionaryVariant(IAvaloniaXamlIlParentStackProvider? stack)
{
switch (stack)
{
case null:
return null;
case IAvaloniaXamlIlEagerParentStackProvider eager:
var enumerator = new EagerParentStackEnumerator(eager);
while (enumerator.TryGetNextOfType<IThemeVariantProvider>() is { } themeVariantProvider)
{
if (themeVariantProvider.Key is { } setKey)
{
return setKey;
}
}
return null;
case { } provider:
foreach (var parent in provider.Parents)
{
if (parent is IThemeVariantProvider { Key: { } setKey })
{
return setKey;
}
}
return null;
}
}
}
}