ContentPresenterTests_InTemplate.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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.Generic;
  5. using System.ComponentModel;
  6. using System.Linq;
  7. using System.Reactive.Linq;
  8. using Avalonia.Controls.Presenters;
  9. using Avalonia.Controls.Templates;
  10. using Avalonia.Data;
  11. using Avalonia.LogicalTree;
  12. using Avalonia.UnitTests;
  13. using Avalonia.VisualTree;
  14. using Moq;
  15. using Xunit;
  16. namespace Avalonia.Controls.UnitTests.Presenters
  17. {
  18. /// <summary>
  19. /// Tests for ContentControls that are hosted in a control template.
  20. /// </summary>
  21. public class ContentPresenterTests_InTemplate
  22. {
  23. [Fact]
  24. public void Should_Register_With_Host_When_TemplatedParent_Set()
  25. {
  26. var host = new Mock<IContentPresenterHost>();
  27. var target = new ContentPresenter();
  28. target.SetValue(Control.TemplatedParentProperty, host.Object);
  29. host.Verify(x => x.RegisterContentPresenter(target));
  30. }
  31. [Fact]
  32. public void Setting_Content_To_Control_Should_Set_Child()
  33. {
  34. var (target, _) = CreateTarget();
  35. var child = new Border();
  36. target.Content = child;
  37. Assert.Equal(child, target.Child);
  38. }
  39. [Fact]
  40. public void Setting_Content_To_Control_Should_Update_Logical_Tree()
  41. {
  42. var (target, parent) = CreateTarget();
  43. var child = new Border();
  44. target.Content = child;
  45. Assert.Equal(parent, child.GetLogicalParent());
  46. Assert.Equal(new[] { child }, parent.GetLogicalChildren());
  47. }
  48. [Fact]
  49. public void Setting_Content_To_Control_Should_Update_Visual_Tree()
  50. {
  51. var (target, _) = CreateTarget();
  52. var child = new Border();
  53. target.Content = child;
  54. Assert.Equal(target, child.GetVisualParent());
  55. Assert.Equal(new[] { child }, target.GetVisualChildren());
  56. }
  57. [Fact]
  58. public void Setting_Content_To_String_Should_Create_TextBlock()
  59. {
  60. var (target, _) = CreateTarget();
  61. target.Content = "Foo";
  62. Assert.IsType<TextBlock>(target.Child);
  63. Assert.Equal("Foo", ((TextBlock)target.Child).Text);
  64. }
  65. [Fact]
  66. public void Setting_Content_To_String_Should_Update_Logical_Tree()
  67. {
  68. var (target, parent) = CreateTarget();
  69. target.Content = "Foo";
  70. var child = target.Child;
  71. Assert.Equal(parent, child.GetLogicalParent());
  72. Assert.Equal(new[] { child }, parent.GetLogicalChildren());
  73. }
  74. [Fact]
  75. public void Setting_Content_To_String_Should_Update_Visual_Tree()
  76. {
  77. var (target, _) = CreateTarget();
  78. target.Content = "Foo";
  79. var child = target.Child;
  80. Assert.Equal(target, child.GetVisualParent());
  81. Assert.Equal(new[] { child }, target.GetVisualChildren());
  82. }
  83. [Fact]
  84. public void Clearing_Control_Content_Should_Update_Logical_Tree()
  85. {
  86. var (target, _) = CreateTarget();
  87. var child = new Border();
  88. target.Content = child;
  89. target.Content = null;
  90. Assert.Null(child.GetLogicalParent());
  91. Assert.Empty(target.GetLogicalChildren());
  92. }
  93. [Fact]
  94. public void Clearing_Control_Content_Should_Update_Visual_Tree()
  95. {
  96. var (target, _) = CreateTarget();
  97. var child = new Border();
  98. target.Content = child;
  99. target.Content = null;
  100. Assert.Null(child.GetVisualParent());
  101. Assert.Empty(target.GetVisualChildren());
  102. }
  103. [Fact]
  104. public void Control_Content_Should_Not_Be_NameScope()
  105. {
  106. var (target, _) = CreateTarget();
  107. target.Content = new TextBlock();
  108. Assert.IsType<TextBlock>(target.Child);
  109. Assert.Null(NameScope.GetNameScope((Control)target.Child));
  110. }
  111. [Fact]
  112. public void Assigning_Control_To_Content_Should_Not_Set_DataContext()
  113. {
  114. var (target, _) = CreateTarget();
  115. target.Content = new Border();
  116. Assert.False(target.IsSet(Control.DataContextProperty));
  117. }
  118. [Fact]
  119. public void Assigning_NonControl_To_Content_Should_Set_DataContext_On_UpdateChild()
  120. {
  121. var (target, _) = CreateTarget();
  122. target.Content = "foo";
  123. Assert.Equal("foo", target.DataContext);
  124. }
  125. [Fact]
  126. public void Should_Use_ContentTemplate_If_Specified()
  127. {
  128. var (target, _) = CreateTarget();
  129. target.ContentTemplate = new FuncDataTemplate<string>((_, __) => new Canvas());
  130. target.Content = "Foo";
  131. Assert.IsType<Canvas>(target.Child);
  132. }
  133. [Fact]
  134. public void Should_Update_If_ContentTemplate_Changed()
  135. {
  136. var (target, _) = CreateTarget();
  137. target.Content = "Foo";
  138. Assert.IsType<TextBlock>(target.Child);
  139. target.ContentTemplate = new FuncDataTemplate<string>((_, __) => new Canvas());
  140. Assert.IsType<Canvas>(target.Child);
  141. target.ContentTemplate = null;
  142. Assert.IsType<TextBlock>(target.Child);
  143. }
  144. [Fact]
  145. public void Assigning_Control_To_Content_After_NonControl_Should_Clear_DataContext()
  146. {
  147. var (target, _) = CreateTarget();
  148. target.Content = "foo";
  149. Assert.True(target.IsSet(Control.DataContextProperty));
  150. target.Content = new Border();
  151. Assert.False(target.IsSet(Control.DataContextProperty));
  152. }
  153. [Fact]
  154. public void Recycles_DataTemplate()
  155. {
  156. var (target, _) = CreateTarget();
  157. target.DataTemplates.Add(new FuncDataTemplate<string>((_, __) => new Border(), true));
  158. target.Content = "foo";
  159. var control = target.Child;
  160. Assert.IsType<Border>(control);
  161. target.Content = "bar";
  162. Assert.Same(control, target.Child);
  163. }
  164. [Fact]
  165. public void Detects_DataTemplate_Doesnt_Match_And_Doesnt_Recycle()
  166. {
  167. var (target, _) = CreateTarget();
  168. target.DataTemplates.Add(new FuncDataTemplate<string>(x => x == "foo", _ => new Border(), true));
  169. target.Content = "foo";
  170. var control = target.Child;
  171. Assert.IsType<Border>(control);
  172. target.Content = "bar";
  173. Assert.IsType<TextBlock>(target.Child);
  174. }
  175. [Fact]
  176. public void Detects_DataTemplate_Doesnt_Support_Recycling()
  177. {
  178. var (target, _) = CreateTarget();
  179. target.DataTemplates.Add(new FuncDataTemplate<string>((_, __) => new Border(), false));
  180. target.Content = "foo";
  181. var control = target.Child;
  182. Assert.IsType<Border>(control);
  183. target.Content = "bar";
  184. Assert.NotSame(control, target.Child);
  185. }
  186. [Fact]
  187. public void Reevaluates_DataTemplates_When_Recycling()
  188. {
  189. var (target, _) = CreateTarget();
  190. target.DataTemplates.Add(new FuncDataTemplate<string>(x => x == "bar", _ => new Canvas(), true));
  191. target.DataTemplates.Add(new FuncDataTemplate<string>((_, __) => new Border(), true));
  192. target.Content = "foo";
  193. var control = target.Child;
  194. Assert.IsType<Border>(control);
  195. target.Content = "bar";
  196. Assert.IsType<Canvas>(target.Child);
  197. }
  198. [Fact]
  199. public void Should_Not_Bind_Old_Child_To_New_DataContext()
  200. {
  201. // Test for issue #1099.
  202. var textBlock = new TextBlock
  203. {
  204. [!TextBlock.TextProperty] = new Binding(),
  205. };
  206. var (target, host) = CreateTarget();
  207. host.DataTemplates.Add(new FuncDataTemplate<string>((_, __) => textBlock));
  208. host.DataTemplates.Add(new FuncDataTemplate<int>((_, __) => new Canvas()));
  209. target.Content = "foo";
  210. Assert.Same(textBlock, target.Child);
  211. textBlock.PropertyChanged += (s, e) =>
  212. {
  213. Assert.NotEqual(e.NewValue, "42");
  214. };
  215. target.Content = 42;
  216. }
  217. [Fact]
  218. public void Should_Not_Bind_Child_To_Wrong_DataContext_When_Removing()
  219. {
  220. // Test for issue #2823
  221. var canvas = new Canvas();
  222. var (target, host) = CreateTarget();
  223. var viewModel = new TestViewModel { Content = "foo" };
  224. var dataContexts = new List<object>();
  225. target.Bind(ContentPresenter.ContentProperty, (IBinding)new TemplateBinding(ContentControl.ContentProperty));
  226. canvas.GetObservable(ContentPresenter.DataContextProperty).Subscribe(x => dataContexts.Add(x));
  227. host.DataTemplates.Add(new FuncDataTemplate<string>((_, __) => canvas));
  228. host.Bind(ContentControl.ContentProperty, new Binding(nameof(TestViewModel.Content)));
  229. host.DataContext = viewModel;
  230. Assert.Same(canvas, target.Child);
  231. viewModel.Content = 42;
  232. Assert.Equal(new object[]
  233. {
  234. null,
  235. "foo",
  236. null,
  237. }, dataContexts);
  238. }
  239. [Fact]
  240. public void Should_Set_InheritanceParent_Even_When_LogicalParent_Is_Already_Set()
  241. {
  242. var logicalParent = new Canvas();
  243. var child = new TextBlock();
  244. var (target, host) = CreateTarget();
  245. ((ISetLogicalParent)child).SetParent(logicalParent);
  246. target.Content = child;
  247. Assert.Same(logicalParent, child.Parent);
  248. // InheritanceParent is exposed via StylingParent.
  249. Assert.Same(target, ((IStyledElement)child).StylingParent);
  250. }
  251. [Fact]
  252. public void Should_Reset_InheritanceParent_When_Child_Removed()
  253. {
  254. var logicalParent = new Canvas();
  255. var child = new TextBlock();
  256. var (target, _) = CreateTarget();
  257. ((ISetLogicalParent)child).SetParent(logicalParent);
  258. target.Content = child;
  259. target.Content = null;
  260. // InheritanceParent is exposed via StylingParent.
  261. Assert.Same(logicalParent, ((IStyledElement)child).StylingParent);
  262. }
  263. [Fact]
  264. public void Should_Clear_Host_When_Host_Template_Cleared()
  265. {
  266. var (target, host) = CreateTarget();
  267. Assert.Same(host, target.Host);
  268. host.Template = null;
  269. host.ApplyTemplate();
  270. Assert.Null(target.Host);
  271. }
  272. (ContentPresenter presenter, ContentControl templatedParent) CreateTarget()
  273. {
  274. var templatedParent = new ContentControl
  275. {
  276. Template = new FuncControlTemplate<ContentControl>((_, s) =>
  277. new ContentPresenter
  278. {
  279. Name = "PART_ContentPresenter",
  280. }.RegisterInNameScope(s)),
  281. };
  282. var root = new TestRoot { Child = templatedParent };
  283. templatedParent.ApplyTemplate();
  284. return ((ContentPresenter)templatedParent.Presenter, templatedParent);
  285. }
  286. private class TestContentControl : ContentControl
  287. {
  288. public IControl Child { get; set; }
  289. }
  290. private class TestViewModel : INotifyPropertyChanged
  291. {
  292. private object _content;
  293. public object Content
  294. {
  295. get => _content;
  296. set
  297. {
  298. if (_content != value)
  299. {
  300. _content = value;
  301. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Content)));
  302. }
  303. }
  304. }
  305. public event PropertyChangedEventHandler PropertyChanged;
  306. }
  307. }
  308. }