ContentControlTests.cs 13 KB

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