ContentControlTests.cs 13 KB

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