File: Platform\ElementExtensions.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)
using System;
using System.Collections.Generic;
#if __IOS__ || MACCATALYST
using PlatformView = UIKit.UIView;
using BasePlatformType = ObjCRuntime.INativeObject;
using PlatformWindow = UIKit.UIWindow;
using PlatformApplication = UIKit.IUIApplicationDelegate;
#elif MONOANDROID
using PlatformView = Android.Views.View;
using BasePlatformType = Android.Content.Context;
using PlatformWindow = Android.App.Activity;
using PlatformApplication = Android.App.Application;
#elif WINDOWS
using PlatformView = Microsoft.UI.Xaml.FrameworkElement;
using BasePlatformType = WinRT.IWinRTObject;
using PlatformWindow = Microsoft.UI.Xaml.Window;
using PlatformApplication = Microsoft.UI.Xaml.Application;
#elif TIZEN
using PlatformView = Tizen.NUI.BaseComponents.View;
using BasePlatformType = System.Object;
using PlatformWindow = Tizen.NUI.Window;
using PlatformApplication = Tizen.Applications.CoreApplication;
#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID && !TIZEN)
using PlatformView = System.Object;
using BasePlatformType = System.Object;
using IPlatformViewHandler = Microsoft.Maui.IViewHandler;
using PlatformWindow = System.Object;
using PlatformApplication = System.Object;
#endif
 
namespace Microsoft.Maui.Platform
{
	public static partial class ElementExtensions
	{
		static HashSet<Type> handlersWithConstructors = new HashSet<Type>();
 
		static IElementHandler? CreateTypeWithInjection(this Type viewType, IMauiContext mauiContext)
		{
			var handlerType = mauiContext.Handlers.GetHandlerType(viewType);
 
			if (handlerType == null)
				return null;
 
#if ANDROID
			if(mauiContext.Context != null)
			{
				return (IElementHandler)Extensions.DependencyInjection.
					ActivatorUtilities.CreateInstance(mauiContext.Services, handlerType, mauiContext.Context);
			}
#endif
 
			return (IElementHandler)Extensions.DependencyInjection.
				ActivatorUtilities.CreateInstance(mauiContext.Services, handlerType);
		}
 
		public static IElementHandler ToHandler(this IElement view, IMauiContext context)
		{
			_ = view ?? throw new ArgumentNullException(nameof(view));
			_ = context ?? throw new ArgumentNullException(nameof(context));
 
			//This is how MVU works. It collapses views down
			if (view is IReplaceableView ir)
				view = ir.ReplacedView;
 
			var handler = view.Handler;
 
			if (handler?.MauiContext != null && handler.MauiContext != context)
				handler = null;
 
 
			// TODO Clean up this handler create. Handlers should probably create through the 
			// DI.Ext Service provider. We just register them all as transient? possibly?
			if (handler == null)
			{
				var viewType = view.GetType();
				try
				{
					if (handlersWithConstructors.Contains(viewType))
						handler = viewType.CreateTypeWithInjection(context);
					else
						handler = context.Handlers.GetHandler(viewType);
				}
				catch (MissingMethodException)
				{
					handler = viewType.CreateTypeWithInjection(context);
					if (handler != null)
						handlersWithConstructors.Add(view.GetType());
				}
			}
 
			if (handler == null)
				throw new HandlerNotFoundException(view);
 
			handler.SetMauiContext(context);
 
			view.Handler = handler;
 
			if (handler.VirtualView != view)
				handler.SetVirtualView(view);
 
			return handler;
		}
 
		internal static PlatformView ToPlatform(this IElement view)
		{
			if (view is IReplaceableView replaceableView && replaceableView.ReplacedView != view)
				return replaceableView.ReplacedView.ToPlatform();
 
 
			_ = view.Handler ?? throw new InvalidOperationException($"{nameof(MauiContext)} should have been set on parent.");
 
			if (view.Handler is IViewHandler viewHandler)
			{
				if (viewHandler.ContainerView is PlatformView containerView)
					return containerView;
 
				if (viewHandler.PlatformView is PlatformView platformView)
					return platformView;
			}
 
			return (view.Handler?.PlatformView as PlatformView) ?? throw new InvalidOperationException($"Unable to convert {view} to {typeof(PlatformView)}");
 
		}
 
		public static PlatformView ToPlatform(this IElement view, IMauiContext context)
		{
			var handler = view.ToHandler(context);
 
			if (handler.PlatformView is not PlatformView result)
			{
				throw new InvalidOperationException($"Unable to convert {view} to {typeof(PlatformView)}");
			}
 
			return view.ToPlatform() ?? throw new InvalidOperationException($"Unable to convert {view} to {typeof(PlatformView)}");
 
		}
 
		static void SetHandler(this BasePlatformType nativeElement, IElement element, IMauiContext context)
		{
			_ = nativeElement ?? throw new ArgumentNullException(nameof(nativeElement));
			_ = element ?? throw new ArgumentNullException(nameof(element));
			_ = context ?? throw new ArgumentNullException(nameof(context));
 
			var handler = element.Handler;
			if (handler?.MauiContext != null && handler.MauiContext != context)
				handler = null;
 
			if (handler == null)
				handler = context.Handlers.GetHandler(element.GetType());
 
			if (handler == null)
				throw new Exception($"Handler not found for window {element}.");
 
			handler.SetMauiContext(context);
 
			element.Handler = handler;
 
			if (handler.VirtualView != element)
				handler.SetVirtualView(element);
		}
 
		public static void SetApplicationHandler(this PlatformApplication platformApplication, IApplication application, IMauiContext context) =>
			SetHandler(platformApplication, application, context);
 
		public static void SetWindowHandler(this PlatformWindow platformWindow, IWindow window, IMauiContext context) =>
			SetHandler(platformWindow, window, context);
 
#if WINDOWS || IOS || ANDROID || TIZEN
		internal static IWindow GetWindow(this IElement element) =>
			element.Handler?.MauiContext?.GetPlatformWindow()?.GetWindow() ??
			throw new InvalidOperationException("IWindow not found");
#endif
 
		internal static T? FindParentOfType<T>(this IElement element, bool includeThis = false)
	where T : IElement
		{
			if (includeThis && element is T view)
				return view;
 
			foreach (var parent in element.GetParentsPath())
			{
				if (parent is T parentView)
					return parentView;
			}
 
			return default;
		}
 
		static IEnumerable<IElement?> GetParentsPath(this IElement self)
		{
			IElement? current = self;
 
			while (current != null && current is not IApplication)
			{
				current = current.Parent;
				yield return current;
			}
		}
	}
}