ContentControlTests.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. using System;
  2. using System.Collections.Specialized;
  3. using System.Linq;
  4. using Moq;
  5. using Avalonia.Controls.Presenters;
  6. using Avalonia.Controls.Templates;
  7. using Avalonia.LogicalTree;
  8. using Avalonia.Styling;
  9. using Avalonia.UnitTests;
  10. using Avalonia.VisualTree;
  11. using Xunit;
  12. using Avalonia.Markup.Data;
  13. using Avalonia.Data;
  14. using System.Collections.Generic;
  15. namespace Avalonia.Controls.UnitTests
  16. {
  17. public class ContentControlTests : ScopedTestBase
  18. {
  19. [Fact]
  20. public void Template_Should_Be_Instantiated()
  21. {
  22. var target = new ContentControl();
  23. target.Content = "Foo";
  24. target.Template = GetTemplate();
  25. target.ApplyTemplate();
  26. target.Presenter.UpdateChild();
  27. var child = target.VisualChildren.Single();
  28. Assert.IsType<Border>(child);
  29. child = child.VisualChildren.Single();
  30. Assert.IsType<ContentPresenter>(child);
  31. child = child.VisualChildren.Single();
  32. Assert.IsType<TextBlock>(child);
  33. }
  34. [Fact]
  35. public void Templated_Children_Should_Be_Styled()
  36. {
  37. var root = new TestRoot
  38. {
  39. Styles =
  40. {
  41. new Style(x => x.Is<Control>())
  42. {
  43. Setters = { new Setter(Control.TagProperty, "foo") }
  44. }
  45. }
  46. };
  47. var target = new ContentControl();
  48. target.Content = "Foo";
  49. target.Template = GetTemplate();
  50. root.Child = target;
  51. target.ApplyTemplate();
  52. target.Presenter.ApplyTemplate();
  53. foreach (Control child in target.GetTemplateChildren())
  54. Assert.Equal("foo", child.Tag);
  55. }
  56. [Fact]
  57. public void ContentPresenter_Should_Have_TemplatedParent_Set()
  58. {
  59. var target = new ContentControl();
  60. var child = new Border();
  61. target.Template = GetTemplate();
  62. target.Content = child;
  63. target.ApplyTemplate();
  64. target.Presenter.UpdateChild();
  65. var contentPresenter = child.GetVisualParent<ContentPresenter>();
  66. Assert.Equal(target, contentPresenter.TemplatedParent);
  67. }
  68. [Fact]
  69. public void Content_Should_Have_TemplatedParent_Set_To_Null()
  70. {
  71. var target = new ContentControl();
  72. var child = new Border();
  73. target.Template = GetTemplate();
  74. target.Content = child;
  75. target.ApplyTemplate();
  76. target.Presenter.UpdateChild();
  77. Assert.Null(child.TemplatedParent);
  78. }
  79. [Fact]
  80. public void Control_Content_Should_Be_Logical_Child_Before_ApplyTemplate()
  81. {
  82. var target = new ContentControl
  83. {
  84. Template = GetTemplate(),
  85. };
  86. var child = new Control();
  87. target.Content = child;
  88. Assert.Equal(child.Parent, target);
  89. Assert.Equal(child.GetLogicalParent(), target);
  90. Assert.Equal(new[] { child }, target.GetLogicalChildren());
  91. }
  92. [Fact]
  93. public void Control_Content_Should_Be_Logical_Child_After_ApplyTemplate()
  94. {
  95. var target = new ContentControl
  96. {
  97. Template = GetTemplate(),
  98. };
  99. var child = new Control();
  100. target.Content = child;
  101. target.ApplyTemplate();
  102. target.Presenter.UpdateChild();
  103. Assert.Equal(child.Parent, target);
  104. Assert.Equal(child.GetLogicalParent(), target);
  105. Assert.Equal(new[] { child }, target.GetLogicalChildren());
  106. }
  107. [Fact]
  108. public void Should_Use_ContentTemplate_To_Create_Control()
  109. {
  110. var target = new ContentControl
  111. {
  112. Template = GetTemplate(),
  113. ContentTemplate = new FuncDataTemplate<string>((_, __) => new Canvas()),
  114. };
  115. target.Content = "Foo";
  116. target.ApplyTemplate();
  117. target.Presenter.UpdateChild();
  118. var child = target.Presenter.Child;
  119. Assert.IsType<Canvas>(child);
  120. }
  121. [Fact]
  122. public void DataTemplate_Created_Control_Should_Be_Logical_Child_After_ApplyTemplate()
  123. {
  124. var target = new ContentControl
  125. {
  126. Template = GetTemplate(),
  127. };
  128. target.Content = "Foo";
  129. target.ApplyTemplate();
  130. target.Presenter.UpdateChild();
  131. var child = target.Presenter.Child;
  132. Assert.NotNull(child);
  133. Assert.Equal(target, child.Parent);
  134. Assert.Equal(target, child.GetLogicalParent());
  135. Assert.Equal(new[] { child }, target.GetLogicalChildren());
  136. }
  137. [Fact]
  138. public void Clearing_Content_Should_Clear_Logical_Child()
  139. {
  140. var target = new ContentControl();
  141. var child = new Control();
  142. target.Content = child;
  143. Assert.Equal(new[] { child }, target.GetLogicalChildren());
  144. target.Content = null;
  145. Assert.Null(child.Parent);
  146. Assert.Null(child.GetLogicalParent());
  147. Assert.Empty(target.GetLogicalChildren());
  148. }
  149. [Fact]
  150. public void Setting_Content_Should_Fire_LogicalChildren_CollectionChanged()
  151. {
  152. var target = new ContentControl();
  153. var child = new Control();
  154. var called = false;
  155. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  156. called = e.Action == NotifyCollectionChangedAction.Add;
  157. target.Template = GetTemplate();
  158. target.Content = child;
  159. target.ApplyTemplate();
  160. target.Presenter.UpdateChild();
  161. Assert.True(called);
  162. }
  163. [Fact]
  164. public void Clearing_Content_Should_Fire_LogicalChildren_CollectionChanged()
  165. {
  166. var target = new ContentControl();
  167. var child = new Control();
  168. var called = false;
  169. target.Template = GetTemplate();
  170. target.Content = child;
  171. target.ApplyTemplate();
  172. target.Presenter.UpdateChild();
  173. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true;
  174. target.Content = null;
  175. target.Presenter.UpdateChild();
  176. Assert.True(called);
  177. }
  178. [Fact]
  179. public void Changing_Content_Should_Fire_LogicalChildren_CollectionChanged()
  180. {
  181. var target = new ContentControl();
  182. var child1 = new Control();
  183. var child2 = new Control();
  184. var called = false;
  185. target.Template = GetTemplate();
  186. target.Content = child1;
  187. target.ApplyTemplate();
  188. target.Presenter.UpdateChild();
  189. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true;
  190. target.Content = child2;
  191. target.Presenter.ApplyTemplate();
  192. Assert.True(called);
  193. }
  194. [Fact]
  195. public void Changing_Content_Should_Update_Presenter()
  196. {
  197. var target = new ContentControl();
  198. target.Template = GetTemplate();
  199. target.ApplyTemplate();
  200. target.Presenter.UpdateChild();
  201. target.Content = "Foo";
  202. target.Presenter.UpdateChild();
  203. Assert.Equal("Foo", ((TextBlock)target.Presenter.Child).Text);
  204. target.Content = "Bar";
  205. target.Presenter.UpdateChild();
  206. Assert.Equal("Bar", ((TextBlock)target.Presenter.Child).Text);
  207. }
  208. [Fact]
  209. public void DataContext_Should_Be_Set_For_DataTemplate_Created_Content()
  210. {
  211. var target = new ContentControl();
  212. target.Template = GetTemplate();
  213. target.Content = "Foo";
  214. target.ApplyTemplate();
  215. target.Presenter.UpdateChild();
  216. Assert.Equal("Foo", target.Presenter.Child.DataContext);
  217. }
  218. [Fact]
  219. public void DataContext_Should_Not_Be_Set_For_Control_Content()
  220. {
  221. var target = new ContentControl();
  222. target.Template = GetTemplate();
  223. target.Content = new TextBlock();
  224. target.ApplyTemplate();
  225. target.Presenter.UpdateChild();
  226. Assert.Null(target.Presenter.Child.DataContext);
  227. }
  228. [Fact]
  229. public void Binding_ContentTemplate_After_Content_Does_Not_Leave_Orpaned_TextBlock()
  230. {
  231. // Test for #1271.
  232. var children = new List<Control>();
  233. var presenter = new ContentPresenter();
  234. // The content and then the content template property need to be bound with delayed bindings
  235. // as they are in Avalonia.Markup.Xaml.
  236. DelayedBinding.Add(presenter, ContentPresenter.ContentProperty, new Binding("Content")
  237. {
  238. Priority = BindingPriority.Template,
  239. RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
  240. });
  241. DelayedBinding.Add(presenter, ContentPresenter.ContentTemplateProperty, new Binding("ContentTemplate")
  242. {
  243. Priority = BindingPriority.Template,
  244. RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent),
  245. });
  246. presenter.GetObservable(ContentPresenter.ChildProperty).Subscribe(children.Add);
  247. var target = new ContentControl
  248. {
  249. Template = new FuncControlTemplate<ContentControl>((_, __) => presenter),
  250. ContentTemplate = new FuncDataTemplate<string>((_, __) => new Canvas()),
  251. Content = "foo",
  252. };
  253. // The control must be rooted.
  254. var root = new TestRoot
  255. {
  256. Child = target,
  257. };
  258. target.ApplyTemplate();
  259. // When the template is applied, the Content property is bound before the ContentTemplate
  260. // property, causing a TextBlock to be created by the default template before ContentTemplate
  261. // is bound.
  262. Assert.Collection(
  263. children,
  264. x => Assert.Null(x),
  265. x => Assert.IsType<TextBlock>(x),
  266. x => Assert.IsType<Canvas>(x));
  267. var textBlock = (TextBlock)children[1];
  268. // The leak in #1271 was caused by the TextBlock's logical parent not being cleared when
  269. // it is replaced by the Canvas.
  270. Assert.Null(textBlock.GetLogicalParent());
  271. }
  272. [Fact]
  273. public void Should_Set_Child_LogicalParent_After_Removing_And_Adding_Back_To_Logical_Tree()
  274. {
  275. var target = new ContentControl();
  276. var root = new TestRoot
  277. {
  278. Styles =
  279. {
  280. new Style(x => x.OfType<ContentControl>())
  281. {
  282. Setters =
  283. {
  284. new Setter(ContentControl.TemplateProperty, GetTemplate()),
  285. }
  286. }
  287. },
  288. Child = target
  289. };
  290. target.Content = "Foo";
  291. target.ApplyTemplate();
  292. target.Presenter.ApplyTemplate();
  293. Assert.Equal(target, target.Presenter.Child.GetLogicalParent());
  294. Assert.Equal(new[] { target.Presenter.Child }, target.LogicalChildren);
  295. root.Child = null;
  296. target.Content = null;
  297. Assert.Empty(target.LogicalChildren);
  298. root.Child = target;
  299. target.Content = "Bar";
  300. Assert.Equal(target, target.Presenter.Child.GetLogicalParent());
  301. Assert.Equal(new[] { target.Presenter.Child }, target.LogicalChildren);
  302. }
  303. private static FuncControlTemplate GetTemplate()
  304. {
  305. return new FuncControlTemplate<ContentControl>((parent, scope) =>
  306. {
  307. return new Border
  308. {
  309. Background = new Media.SolidColorBrush(0xffffffff),
  310. Child = new ContentPresenter
  311. {
  312. Name = "PART_ContentPresenter",
  313. [~ContentPresenter.ContentProperty] = parent[~ContentControl.ContentProperty],
  314. [~ContentPresenter.ContentTemplateProperty] = parent[~ContentControl.ContentTemplateProperty],
  315. }.RegisterInNameScope(scope)
  316. };
  317. });
  318. }
  319. }
  320. }