diff options
Diffstat (limited to 'Xamarin.Forms.Core/TemplateUtilities.cs')
-rw-r--r-- | Xamarin.Forms.Core/TemplateUtilities.cs | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/Xamarin.Forms.Core/TemplateUtilities.cs b/Xamarin.Forms.Core/TemplateUtilities.cs new file mode 100644 index 00000000..0599e747 --- /dev/null +++ b/Xamarin.Forms.Core/TemplateUtilities.cs @@ -0,0 +1,123 @@ +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<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) + { + 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<Element>(16); + queue.Enqueue((Element)self); + + while (queue.Count > 0) + { + ReadOnlyCollection<Element> 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); + } + } +}
\ No newline at end of file |