ContentPresenterTests_InTemplate.cs 13 KB

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