File: MarkupExtensions\OnIdiomExtension.cs
Web Access
Project: src\src\Controls\src\Xaml\Controls.Xaml.csproj (Microsoft.Maui.Controls.Xaml)
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Xml;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Maui.Devices;
 
namespace Microsoft.Maui.Controls.Xaml
{
	[ContentProperty(nameof(Default))]
	[RequireService(
		[typeof(IProvideValueTarget),
		 typeof(IValueConverterProvider),
		 typeof(IXmlLineInfoProvider),
		 typeof(IConverterOptions)])]
	[RequiresUnreferencedCode("The OnIdiomExtension is not trim safe. Use OnIdiom<T> instead.")]
	public class OnIdiomExtension : IMarkupExtension
	{
		// See Device.Idiom
 
		public object Default { get; set; }
		public object Phone { get; set; }
		public object Tablet { get; set; }
		public object Desktop { get; set; }
		public object TV { get; set; }
		public object Watch { get; set; }
 
		public IValueConverter Converter { get; set; }
 
		public object ConverterParameter { get; set; }
 
		public object ProvideValue(IServiceProvider serviceProvider)
		{
			if (Default == null
				&& Phone == null
				&& Tablet == null
				&& Desktop == null
				&& TV == null
				&& Watch == null)
				throw new XamlParseException("OnIdiomExtension requires a non-null value to be specified for at least one idiom or Default.", serviceProvider);
 
			var valueProvider = serviceProvider?.GetService<IProvideValueTarget>() ?? throw new ArgumentException();
 
			BindableProperty bp;
			PropertyInfo pi = null;
			Type propertyType = null;
 
			if (valueProvider.TargetObject is Setter setter)
			{
				bp = setter.Property;
			}
			else
			{
				bp = valueProvider.TargetProperty as BindableProperty;
				pi = valueProvider.TargetProperty as PropertyInfo;
			}
			propertyType = bp?.ReturnType
							  ?? pi?.PropertyType
							  ?? throw new InvalidOperationException("Cannot determine property to provide the value for.");
 
			var value = GetValue();
			if (value == null && propertyType.IsValueType)
				return Activator.CreateInstance(propertyType);
 
			if (Converter != null)
				return Converter.Convert(value, propertyType, ConverterParameter, CultureInfo.CurrentUICulture);
 
			var converterProvider = serviceProvider?.GetService<IValueConverterProvider>();
			if (converterProvider != null)
			{
				MemberInfo minforetriever()
				{
					if (pi != null)
						return pi;
 
					MemberInfo minfo = null;
					try
					{
						minfo = bp.DeclaringType.GetRuntimeProperty(bp.PropertyName);
					}
					catch (AmbiguousMatchException e)
					{
						throw new XamlParseException($"Multiple properties with name '{bp.DeclaringType}.{bp.PropertyName}' found.", serviceProvider, innerException: e);
					}
					if (minfo != null)
						return minfo;
					try
					{
						return bp.DeclaringType.GetRuntimeMethod("Get" + bp.PropertyName, new[] { typeof(BindableObject) });
					}
					catch (AmbiguousMatchException e)
					{
						throw new XamlParseException($"Multiple methods with name '{bp.DeclaringType}.Get{bp.PropertyName}' found.", serviceProvider, innerException: e);
					}
				}
 
				return converterProvider.Convert(value, propertyType, minforetriever, serviceProvider);
			}
			if (converterProvider != null)
				return converterProvider.Convert(value, propertyType, () => pi, serviceProvider);
 
			var ret = value.ConvertTo(propertyType, () => pi, serviceProvider, out Exception exception);
			if (exception != null)
				throw exception;
			return ret;
		}
 
		object GetValue()
		{
			if (DeviceInfo.Idiom == DeviceIdiom.Phone)
				return Phone ?? Default;
			if (DeviceInfo.Idiom == DeviceIdiom.Tablet)
				return Tablet ?? Default;
			if (DeviceInfo.Idiom == DeviceIdiom.Desktop)
				return Desktop ?? Default;
			if (DeviceInfo.Idiom == DeviceIdiom.TV)
				return TV ?? Default;
			if (DeviceInfo.Idiom == DeviceIdiom.Watch)
				return Watch ?? Default;
			return Default;
		}
	}
}