using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading.Tasks; namespace Xamarin.Forms { internal static class TemplateUtilities { public static async Task 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 GetRealParentAsync(Element element) { Element parent = element.RealParent; if (parent is Application) return Task.FromResult(null); if (parent != null) return Task.FromResult(parent); var tcs = new TaskCompletionSource(); 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) { for (var i = 0; i < self.InternalChildren.Count; i++) { self.InternalChildren.Remove(self.InternalChildren[i]); } 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(16); queue.Enqueue((Element)self); while (queue.Count > 0) { ReadOnlyCollection children = queue.Dequeue().LogicalChildren; 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 for (var i = 0; i < self.InternalChildren.Count; i++) { self.InternalChildren.Remove(self.InternalChildren[i]); } ControlTemplate template = self.ControlTemplate; var content = template.CreateContent() as View; if (content == null) { throw new NotSupportedException("ControlTemplate must return a type derived from View."); } self.InternalChildren.Add(content); } } }