File: Handlers\Element\ElementHandler.cs
Web Access
Project: src\src\Core\src\Core.csproj (Microsoft.Maui)
using System;
 
namespace Microsoft.Maui.Handlers
{
	public abstract partial class ElementHandler : IElementHandler
	{
		public static IPropertyMapper<IElement, IElementHandler> ElementMapper = new PropertyMapper<IElement, IElementHandler>()
		{
		};
 
		public static CommandMapper<IElement, IElementHandler> ElementCommandMapper = new CommandMapper<IElement, IElementHandler>()
		{
		};
 
		internal readonly IPropertyMapper _defaultMapper;
		internal readonly CommandMapper? _commandMapper;
		internal IPropertyMapper _mapper;
 
		protected ElementHandler(IPropertyMapper mapper, CommandMapper? commandMapper = null)
		{
			_ = mapper ?? throw new ArgumentNullException(nameof(mapper));
			_defaultMapper = mapper;
			_mapper = _defaultMapper;
			_commandMapper = commandMapper;
		}
 
		public IMauiContext? MauiContext { get; private set; }
 
		public IServiceProvider? Services => MauiContext?.Services;
 
		public object? PlatformView { get; private protected set; }
 
		public IElement? VirtualView { get; private protected set; }
 
		public virtual void SetMauiContext(IMauiContext mauiContext) =>
			MauiContext = mauiContext;
 
		public virtual void SetVirtualView(IElement view)
		{
			_ = view ?? throw new ArgumentNullException(nameof(view));
 
			if (VirtualView == view)
				return;
 
			var oldVirtualView = VirtualView;
 
			bool setupPlatformView = oldVirtualView == null;
 
			VirtualView = view;
			PlatformView ??= CreatePlatformElement();
 
			if (VirtualView.Handler != this)
				VirtualView.Handler = this;
 
			// We set the previous virtual view to null after setting it on the incoming virtual view.
			// This makes it easier for the incoming virtual view to have influence
			// on how the exchange of handlers happens.
			// We will just set the handler to null ourselves as a last resort cleanup
			if (oldVirtualView?.Handler != null)
				oldVirtualView.Handler = null;
 
			if (setupPlatformView)
			{
				ConnectHandler(PlatformView);
			}
 
			_mapper = _defaultMapper;
 
			if (VirtualView is IPropertyMapperView imv)
			{
				var map = imv.GetPropertyMapperOverrides();
				if (map is not null)
				{
					map.Chained = new[] { _defaultMapper };
					_mapper = map;
				}
			}
 
			_mapper.UpdateProperties(this, VirtualView);
		}
 
		public virtual void UpdateValue(string property)
		{
			if (VirtualView == null)
				return;
 
			_mapper?.UpdateProperty(this, VirtualView, property);
		}
 
		public virtual void Invoke(string command, object? args)
		{
			if (VirtualView == null)
				return;
 
			_commandMapper?.Invoke(this, VirtualView, command, args);
		}
 
		private protected abstract object OnCreatePlatformElement();
 
		object CreatePlatformElement() =>
			OnCreatePlatformElement();
 
		private protected abstract void OnConnectHandler(object platformView);
 
		void ConnectHandler(object platformView) =>
			OnConnectHandler(platformView);
 
		private protected abstract void OnDisconnectHandler(object platformView);
 
		void DisconnectHandler(object platformView)
		{
			OnDisconnectHandler(platformView);
 
			// VirtualView has already been changed over to a new handler
			if (VirtualView != null && VirtualView.Handler == this)
				VirtualView.Handler = null;
 
			VirtualView = null;
		}
 
		void IElementHandler.DisconnectHandler()
		{
			if (PlatformView != null && VirtualView != null)
			{
				// We set the PlatformView to null so no one outside of this handler tries to access
				// PlatformView. PlatformView access should be isolated to the instance passed into
				// DisconnectHandler
				var oldPlatformView = PlatformView;
				PlatformView = null;
				DisconnectHandler(oldPlatformView);
			}
		}
	}
}