File: TemplateUtilities.cs
Web Access
Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls)
#nullable disable
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
 
namespace Microsoft.Maui.Controls
{
	internal static class TemplateUtilities
	{
		public static async Task<Element> FindTemplatedParentAsync(Element element)
		{
			if (element.RealParent is Application)
				return null;
 
			var skipCount = 0;
			element = await GetRealParentAsync(element);
			while (!Application.IsApplicationOrNull(element))
			{
				var controlTemplated = element as IControlTemplated;
				if (controlTemplated?.ControlTemplate != null)
				{
					if (skipCount == 0)
						return element;
					skipCount--;
				}
				if (element is ContentPresenter)
					skipCount++;
				element = await GetRealParentAsync(element);
			}
 
			return null;
		}
 
		public static Task<Element> GetRealParentAsync(Element element)
		{
			Element parent = element.RealParent;
			if (parent is Application)
				return Task.FromResult<Element>(null);
 
			if (parent != null)
				return Task.FromResult(parent);
 
			var tcs = new TaskCompletionSource<Element>();
			EventHandler handler = null;
			handler = (sender, args) =>
			{
				tcs.TrySetResult(element.RealParent);
				element.ParentSet -= handler;
			};
			element.ParentSet += handler;
 
			return tcs.Task;
		}
 
		public static void OnContentChanged(BindableObject bindable, object oldValue, object newValue)
		{
			var self = (IControlTemplated)bindable;
			var newElement = (Element)newValue;
			if (self.ControlTemplate == null)
			{
				while (self.InternalChildren.Count > 0)
				{
					self.InternalChildren.RemoveAt(self.InternalChildren.Count - 1);
				}
 
				if (newValue != null)
					self.InternalChildren.Add(newElement);
			}
			else
			{
				if (newElement != null)
				{
					BindableObject.SetInheritedBindingContext(newElement, bindable.BindingContext);
				}
			}
		}
 
		public static void OnControlTemplateChanged(BindableObject bindable, object oldValue, object newValue)
		{
			var self = (IControlTemplated)bindable;
 
			// First make sure any old ContentPresenters are no longer bound up. This MUST be
			// done before we attempt to make the new template.
			if (oldValue != null)
			{
				var queue = new Queue<Element>(16);
				queue.Enqueue((Element)self);
 
				while (queue.Count > 0)
				{
					IReadOnlyList<Element> children = queue.Dequeue().LogicalChildrenInternal;
					for (var i = 0; i < children.Count; i++)
					{
						Element child = children[i];
						var controlTemplated = child as IControlTemplated;
 
						var presenter = child as ContentPresenter;
						if (presenter != null)
							presenter.Clear();
						else if (controlTemplated == null || controlTemplated.ControlTemplate == null)
							queue.Enqueue(child);
					}
				}
			}
 
			// Now remove all remnants of any other children just to be sure
			while (self.InternalChildren.Count > 0)
			{
				self.InternalChildren.RemoveAt(self.InternalChildren.Count - 1);
			}
 
			ControlTemplate template = self.ControlTemplate;
			if (template == null)
			{
				// do nothing for now
			}
			else
			{
				var content = template.CreateContent() as View;
				if (content == null)
				{
					throw new NotSupportedException("ControlTemplate must return a type derived from View.");
				}
 
				self.InternalChildren.Add(content);
				var controlTemplated = (IControlTemplated)bindable;
				controlTemplated.OnControlTemplateChanged((ControlTemplate)oldValue, (ControlTemplate)newValue);
				controlTemplated.TemplateRoot = content;
				controlTemplated.OnApplyTemplate();
			}
		}
 
		public static object GetTemplateChild(this IControlTemplated controlTemplated, string name)
		{
			return controlTemplated.TemplateRoot?.FindByName(name);
		}
 
		internal static void OnChildRemoved(IControlTemplated controlTemplated, Element removedChild)
		{
			if (removedChild == controlTemplated.TemplateRoot)
				controlTemplated.TemplateRoot = null;
		}
	}
}