File: MarkupExtensionParser.cs
Web Access
Project: src\src\Controls\src\Xaml\Controls.Xaml.csproj (Microsoft.Maui.Controls.Xaml)
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
 
namespace Microsoft.Maui.Controls.Xaml
{
	[RequiresUnreferencedCode(TrimmerConstants.XamlRuntimeParsingNotSupportedWarning)]
#if !NETSTANDARD
	[RequiresDynamicCode(TrimmerConstants.XamlRuntimeParsingNotSupportedWarning)]
#endif
	internal sealed class MarkupExtensionParser : MarkupExpressionParser, IExpressionParser<object>
	{
		IMarkupExtension markupExtension;
 
		public object Parse(string match, ref string remaining, IServiceProvider serviceProvider)
		{
			var typeResolver = serviceProvider.GetService(typeof(IXamlTypeResolver)) as IXamlTypeResolver;
 
			//shortcut for Binding and StaticResource, to avoid too many reflection calls.
			if (match == "Binding")
				markupExtension = new BindingExtension();
			else if (match == "TemplateBinding")
				markupExtension = new TemplateBindingExtension();
			else if (match == "StaticResource")
				markupExtension = new StaticResourceExtension();
			else if (match == "OnPlatform")
				markupExtension = new OnPlatformExtension();
			else if (match == "OnIdiom")
				markupExtension = new OnIdiomExtension();
			else if (match == "AppThemeBinding")
				markupExtension = new AppThemeBindingExtension();
			else if (match == "DataTemplate")
				markupExtension = new DataTemplateExtension();
			else
			{
				if (typeResolver == null)
					return null;
				Type type;
 
				//The order of lookup is to look for the Extension-suffixed class name first and then look for the class name without the Extension suffix.
				if (!typeResolver.TryResolve(match + "Extension", out type) && !typeResolver.TryResolve(match, out type))
					throw new XamlParseException($"MarkupExtension not found for {match}", serviceProvider);
				markupExtension = Activator.CreateInstance(type) as IMarkupExtension;
			}
 
			if (markupExtension == null)
				throw new XamlParseException($"Missing public default constructor for MarkupExtension {match}", serviceProvider);
 
			if (remaining == "}")
				return markupExtension.ProvideValue(serviceProvider);
 
			Property value;
			do
			{
				value = ParseProperty(serviceProvider, ref remaining);
				SetPropertyValue(value.name, value.strValue, value.value, serviceProvider);
			}
			while (!value.last);
 
			return markupExtension.ProvideValue(serviceProvider);
		}
 
		private void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider)
		{
			MethodInfo setter;
			if (prop == null)
			{
				//implicit property
				var t = markupExtension.GetType();
				prop = ApplyPropertiesVisitor.GetContentPropertyName(t);
				if (prop == null)
					return;
				try
				{
					setter = t.GetRuntimeProperty(prop).SetMethod;
				}
				catch (AmbiguousMatchException e)
				{
					throw new XamlParseException($"Multiple properties with name  '{t}.{prop}' found.", serviceProvider, innerException: e);
				}
			}
			else
			{
				try
				{
					setter = markupExtension.GetType().GetRuntimeProperty(prop).SetMethod;
				}
				catch (AmbiguousMatchException e)
				{
					throw new XamlParseException($"Multiple properties with name  '{markupExtension.GetType()}.{prop}' found.", serviceProvider, innerException: e);
				}
 
			}
			if (value == null && strValue != null)
			{
				try
				{
					value = strValue.ConvertTo(markupExtension.GetType().GetRuntimeProperty(prop).PropertyType,
						(Func<TypeConverter>)null, serviceProvider, out Exception converterException);
					if (converterException != null)
						throw converterException;
				}
				catch (AmbiguousMatchException e)
				{
					throw new XamlParseException($"Multiple properties with name  '{markupExtension.GetType()}.{prop}' found.", serviceProvider, innerException: e);
				}
			}
 
			setter.Invoke(markupExtension, new[] { value });
		}
	}
}