summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Core/TemplateUtilities.cs
blob: 4a8d21b9e29206b0c5ea73942fe49401bfb3b0ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
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().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
			for (var i = 0; i < self.InternalChildren.Count; i++)
			{
				self.InternalChildren.Remove(self.InternalChildren[i]);
			}

			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);
			}
		}
	}
}