summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core/TemplateUtilities.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Core/TemplateUtilities.cs')
-rw-r--r--Xamarin.Forms.Core/TemplateUtilities.cs123
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