summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Xaml/MarkupExtensions/StaticResourceExtension.cs
blob: 4f1465d31ec555fd80685d2becd2fdd1ccd7b65f (plain)
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
using System;
using System.Reflection;

namespace Xamarin.Forms.Xaml
{
	[ContentProperty("Key")]
	public sealed class StaticResourceExtension : IMarkupExtension
	{
		public string Key { get; set; }

		public object ProvideValue(IServiceProvider serviceProvider)
		{
			if (serviceProvider == null)
				throw new ArgumentNullException("serviceProvider");
			if (Key == null)
			{
				var lineInfoProvider = serviceProvider.GetService(typeof (IXmlLineInfoProvider)) as IXmlLineInfoProvider;
				var lineInfo = (lineInfoProvider != null) ? lineInfoProvider.XmlLineInfo : new XmlLineInfo();
				throw new XamlParseException("you must specify a key in {StaticResource}", lineInfo);
			}
			var valueProvider = serviceProvider.GetService(typeof (IProvideValueTarget)) as IProvideParentValues;
			if (valueProvider == null)
				throw new ArgumentException();
			var xmlLineInfoProvider = serviceProvider.GetService(typeof (IXmlLineInfoProvider)) as IXmlLineInfoProvider;
			var xmlLineInfo = xmlLineInfoProvider != null ? xmlLineInfoProvider.XmlLineInfo : null;

			foreach (var p in valueProvider.ParentObjects)
			{
				var ve = p as VisualElement;
				if (ve == null)
					continue;
				if (ve.Resources == null)
					continue;
				object res;
				if (ve.Resources.TryGetValue(Key, out res))
				{
					return ConvertCompiledOnPlatform(res);
				}
			}
			if (Application.Current != null && Application.Current.Resources != null &&
			    Application.Current.Resources.ContainsKey(Key))
			{
				var resource = Application.Current.Resources[Key];

				return ConvertCompiledOnPlatform(resource);
			}

			throw new XamlParseException($"StaticResource not found for key {Key}", xmlLineInfo);
		}

		static object ConvertCompiledOnPlatform(object resource)
		{
			var actualType = resource.GetType();
			if (actualType.GetTypeInfo().IsGenericType && actualType.GetGenericTypeDefinition() == typeof(OnPlatform<>))
			{
				// If we're accessing OnPlatform via a StaticResource in compiled XAML 
				// we'll have to handle the cast to the target type manually 
				// (Normally the compiled XAML handles this by calling `implicit` explicitly,
				// but it doesn't know to do that when it's using a static resource)
				var method = actualType.GetRuntimeMethod("op_Implicit", new[] { actualType });
				resource = method.Invoke(resource, new[] { resource });
			}

			return resource;
		}
	}
}