File: MarkupExtensions\OnPlatformExtension.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 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 OnPlatformExtension is not trim safe. Use OnPlatform<T> instead.")]
	public class OnPlatformExtension : IMarkupExtension
	{
		static object s_notset = new object();
 
		public object Default { get; set; } = s_notset;
		public object Android { get; set; } = s_notset;
		internal object GTK { get; set; } = s_notset;
		public object iOS { get; set; } = s_notset;
		internal object macOS { get; set; } = s_notset;
		public object MacCatalyst { get; set; } = s_notset;
		public object Tizen { get; set; } = s_notset;
		[Obsolete("Use WinUI instead.")]
		public object UWP { get; set; } = s_notset;
		internal object WPF { get; set; } = s_notset;
		public object WinUI { get; set; } = s_notset;
 
		public IValueConverter Converter { get; set; }
 
		public object ConverterParameter { get; set; }
 
		public object ProvideValue(IServiceProvider serviceProvider)
		{
			if (Android == s_notset
				&& GTK == s_notset
				&& iOS == s_notset
				&& macOS == s_notset
				&& MacCatalyst == s_notset
				&& Tizen == s_notset
#pragma warning disable CS0618 // Type or member is obsolete
				&& UWP == s_notset
#pragma warning restore CS0618 // Type or member is obsolete
				&& WPF == s_notset
				&& WinUI == s_notset
				&& Default == s_notset)
			{
				throw new XamlParseException("OnPlatformExtension requires a value to be specified for at least one platform 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.");
 
			if (!TryGetValueForPlatform(out var value))
			{
				if (bp != null)
				{
					object targetObject = valueProvider.TargetObject;
 
					if (targetObject is Setter)
						return null;
					else
						return bp.GetDefaultValue(targetObject as BindableObject);
				}
				if (propertyType.IsValueType)
					return Activator.CreateInstance(propertyType);
				return null;
			}
 
			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);
			}
			var ret = value.ConvertTo(propertyType, () => pi, serviceProvider, out Exception exception);
			if (exception != null)
				throw exception;
			return ret;
		}
 
		bool TryGetValueForPlatform(out object value)
		{
			if (DeviceInfo.Platform == DevicePlatform.Android && Android != s_notset)
			{
				value = Android;
				return true;
			}
			if (DeviceInfo.Platform == DevicePlatform.Create("GTK") && GTK != s_notset)
			{
				value = GTK;
				return true;
			}
			if (DeviceInfo.Platform == DevicePlatform.iOS && iOS != s_notset)
			{
				value = iOS;
				return true;
			}
			if (DeviceInfo.Platform == DevicePlatform.macOS && macOS != s_notset)
			{
				value = macOS;
				return true;
			}
			if (DeviceInfo.Platform == DevicePlatform.MacCatalyst && MacCatalyst != s_notset)
			{
				value = MacCatalyst;
				return true;
			}
			if (DeviceInfo.Platform == DevicePlatform.Tizen && Tizen != s_notset)
			{
				value = Tizen;
				return true;
			}
			if (DeviceInfo.Platform == DevicePlatform.WinUI && WinUI != s_notset)
			{
				value = WinUI;
				return true;
			}
#pragma warning disable CS0618 // Type or member is obsolete
			if (DeviceInfo.Platform == DevicePlatform.WinUI && UWP != s_notset)
			{
				value = UWP;
				return true;
			}
			if (DeviceInfo.Platform == DevicePlatform.Create("UWP") && UWP != s_notset)
			{
				value = UWP;
				return true;
			}
#pragma warning restore CS0618 // Type or member is obsolete
			if (DeviceInfo.Platform == DevicePlatform.Create("WPF") && WPF != s_notset)
			{
				value = WPF;
				return true;
			}
			value = Default;
			return value != s_notset;
		}
	}
}