TemplatedControlTests.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  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.Linq;
  6. using Moq;
  7. using Avalonia.Controls.Presenters;
  8. using Avalonia.Controls.Primitives;
  9. using Avalonia.Controls.Templates;
  10. using Avalonia.LogicalTree;
  11. using Avalonia.Styling;
  12. using Avalonia.UnitTests;
  13. using Avalonia.VisualTree;
  14. using Xunit;
  15. namespace Avalonia.Controls.UnitTests.Primitives
  16. {
  17. public class TemplatedControlTests
  18. {
  19. [Fact]
  20. public void Template_Doesnt_Get_Executed_On_Set()
  21. {
  22. bool executed = false;
  23. var template = new FuncControlTemplate((_, __) =>
  24. {
  25. executed = true;
  26. return new Control();
  27. });
  28. var target = new TemplatedControl
  29. {
  30. Template = template,
  31. };
  32. Assert.False(executed);
  33. }
  34. [Fact]
  35. public void Template_Gets_Executed_On_Measure()
  36. {
  37. bool executed = false;
  38. var template = new FuncControlTemplate((_, __) =>
  39. {
  40. executed = true;
  41. return new Control();
  42. });
  43. var target = new TemplatedControl
  44. {
  45. Template = template,
  46. };
  47. target.Measure(new Size(100, 100));
  48. Assert.True(executed);
  49. }
  50. [Fact]
  51. public void ApplyTemplate_Should_Create_Visual_Children()
  52. {
  53. var target = new TemplatedControl
  54. {
  55. Template = new FuncControlTemplate((_, __) => new Decorator
  56. {
  57. Child = new Panel
  58. {
  59. Children =
  60. {
  61. new TextBlock(),
  62. new Border(),
  63. }
  64. }
  65. }),
  66. };
  67. target.ApplyTemplate();
  68. var types = target.GetVisualDescendants().Select(x => x.GetType()).ToList();
  69. Assert.Equal(
  70. new[]
  71. {
  72. typeof(Decorator),
  73. typeof(Panel),
  74. typeof(TextBlock),
  75. typeof(Border)
  76. },
  77. types);
  78. Assert.Empty(target.GetLogicalChildren());
  79. }
  80. [Fact]
  81. public void Templated_Children_Should_Have_TemplatedParent_Set()
  82. {
  83. var target = new TemplatedControl
  84. {
  85. Template = new FuncControlTemplate((_, __) => new Decorator
  86. {
  87. Child = new Panel
  88. {
  89. Children =
  90. {
  91. new TextBlock(),
  92. new Border(),
  93. }
  94. }
  95. }),
  96. };
  97. target.ApplyTemplate();
  98. var templatedParents = target.GetVisualDescendants()
  99. .OfType<IControl>()
  100. .Select(x => x.TemplatedParent)
  101. .ToList();
  102. Assert.Equal(4, templatedParents.Count);
  103. Assert.True(templatedParents.All(x => x == target));
  104. }
  105. [Fact]
  106. public void Templated_Child_Should_Have_Parent_Set()
  107. {
  108. var target = new TemplatedControl
  109. {
  110. Template = new FuncControlTemplate((_, __) => new Decorator())
  111. };
  112. target.ApplyTemplate();
  113. var child = (Decorator)target.GetVisualChildren().Single();
  114. Assert.Equal(target, child.Parent);
  115. Assert.Equal(target, child.GetLogicalParent());
  116. }
  117. [Fact]
  118. public void Changing_Template_Should_Clear_Old_Templated_Childs_Parent()
  119. {
  120. var target = new TemplatedControl
  121. {
  122. Template = new FuncControlTemplate((_, __) => new Decorator())
  123. };
  124. target.ApplyTemplate();
  125. var child = (Decorator)target.GetVisualChildren().Single();
  126. target.Template = new FuncControlTemplate((_, __) => new Canvas());
  127. target.ApplyTemplate();
  128. Assert.Null(child.Parent);
  129. }
  130. [Fact]
  131. public void Nested_Templated_Control_Should_Not_Have_Template_Applied()
  132. {
  133. var target = new TemplatedControl
  134. {
  135. Template = new FuncControlTemplate((_, __) => new ScrollViewer())
  136. };
  137. target.ApplyTemplate();
  138. var child = (ScrollViewer)target.GetVisualChildren().Single();
  139. Assert.Empty(child.GetVisualChildren());
  140. }
  141. [Fact]
  142. public void Templated_Children_Should_Be_Styled()
  143. {
  144. using (UnitTestApplication.Start(TestServices.MockStyler))
  145. {
  146. TestTemplatedControl target;
  147. var root = new TestRoot
  148. {
  149. Child = target = new TestTemplatedControl
  150. {
  151. Template = new FuncControlTemplate((_, __) =>
  152. {
  153. return new StackPanel
  154. {
  155. Children =
  156. {
  157. new TextBlock
  158. {
  159. }
  160. }
  161. };
  162. }),
  163. }
  164. };
  165. target.ApplyTemplate();
  166. var styler = Mock.Get(UnitTestApplication.Current.Services.Styler);
  167. styler.Verify(x => x.ApplyStyles(It.IsAny<TestTemplatedControl>()), Times.Once());
  168. styler.Verify(x => x.ApplyStyles(It.IsAny<StackPanel>()), Times.Once());
  169. styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
  170. }
  171. }
  172. [Fact]
  173. public void Nested_Templated_Controls_Have_Correct_TemplatedParent()
  174. {
  175. var target = new TestTemplatedControl
  176. {
  177. Template = new FuncControlTemplate((_, __) =>
  178. {
  179. return new ContentControl
  180. {
  181. Template = new FuncControlTemplate((parent, ___) =>
  182. {
  183. return new Border
  184. {
  185. Child = new ContentPresenter
  186. {
  187. [~ContentPresenter.ContentProperty] = parent.GetObservable(ContentControl.ContentProperty).ToBinding(),
  188. }
  189. };
  190. }),
  191. Content = new Decorator
  192. {
  193. Child = new TextBlock()
  194. }
  195. };
  196. }),
  197. };
  198. target.ApplyTemplate();
  199. var contentControl = target.GetTemplateChildren().OfType<ContentControl>().Single();
  200. contentControl.ApplyTemplate();
  201. var border = contentControl.GetTemplateChildren().OfType<Border>().Single();
  202. var presenter = contentControl.GetTemplateChildren().OfType<ContentPresenter>().Single();
  203. var decorator = (Decorator)presenter.Content;
  204. var textBlock = (TextBlock)decorator.Child;
  205. Assert.Equal(target, contentControl.TemplatedParent);
  206. Assert.Equal(contentControl, border.TemplatedParent);
  207. Assert.Equal(contentControl, presenter.TemplatedParent);
  208. Assert.Equal(target, decorator.TemplatedParent);
  209. Assert.Equal(target, textBlock.TemplatedParent);
  210. }
  211. [Fact]
  212. public void ApplyTemplate_Should_Raise_TemplateApplied()
  213. {
  214. var target = new TestTemplatedControl
  215. {
  216. Template = new FuncControlTemplate((_, __) => new Decorator())
  217. };
  218. var raised = false;
  219. target.TemplateApplied += (s, e) =>
  220. {
  221. Assert.Equal(TemplatedControl.TemplateAppliedEvent, e.RoutedEvent);
  222. Assert.Same(target, e.Source);
  223. Assert.NotNull(e.NameScope);
  224. raised = true;
  225. };
  226. target.ApplyTemplate();
  227. Assert.True(raised);
  228. }
  229. [Fact]
  230. public void Applying_New_Template_Clears_TemplatedParent_Of_Old_Template_Children()
  231. {
  232. var target = new TestTemplatedControl
  233. {
  234. Template = new FuncControlTemplate((_, __) => new Decorator
  235. {
  236. Child = new Border(),
  237. })
  238. };
  239. target.ApplyTemplate();
  240. var decorator = (Decorator)target.GetVisualChildren().Single();
  241. var border = (Border)decorator.Child;
  242. Assert.Equal(target, decorator.TemplatedParent);
  243. Assert.Equal(target, border.TemplatedParent);
  244. target.Template = new FuncControlTemplate((_, __) => new Canvas());
  245. // Templated children should not be removed here: the control may be re-added
  246. // somewhere with the same template, so they could still be of use.
  247. Assert.Same(decorator, target.GetVisualChildren().Single());
  248. Assert.Equal(target, decorator.TemplatedParent);
  249. Assert.Equal(target, border.TemplatedParent);
  250. target.ApplyTemplate();
  251. Assert.Null(decorator.TemplatedParent);
  252. Assert.Null(border.TemplatedParent);
  253. }
  254. [Fact]
  255. public void TemplateChild_AttachedToLogicalTree_Should_Be_Raised()
  256. {
  257. Border templateChild = new Border();
  258. var root = new TestRoot
  259. {
  260. Child = new TestTemplatedControl
  261. {
  262. Template = new FuncControlTemplate((_, __) => new Decorator
  263. {
  264. Child = templateChild,
  265. })
  266. }
  267. };
  268. var raised = false;
  269. templateChild.AttachedToLogicalTree += (s, e) => raised = true;
  270. root.Child.ApplyTemplate();
  271. Assert.True(raised);
  272. }
  273. [Fact]
  274. public void TemplateChild_DetachedFromLogicalTree_Should_Be_Raised()
  275. {
  276. Border templateChild = new Border();
  277. var root = new TestRoot
  278. {
  279. Child = new TestTemplatedControl
  280. {
  281. Template = new FuncControlTemplate((_, __) => new Decorator
  282. {
  283. Child = templateChild,
  284. })
  285. }
  286. };
  287. root.Child.ApplyTemplate();
  288. var raised = false;
  289. templateChild.DetachedFromLogicalTree += (s, e) => raised = true;
  290. root.Child = null;
  291. Assert.True(raised);
  292. }
  293. [Fact]
  294. public void Removing_From_LogicalTree_Should_Not_Remove_Child()
  295. {
  296. using (UnitTestApplication.Start(TestServices.RealStyler))
  297. {
  298. Border templateChild = new Border();
  299. TestTemplatedControl target;
  300. var root = new TestRoot
  301. {
  302. Styles =
  303. {
  304. new Style(x => x.OfType<TestTemplatedControl>())
  305. {
  306. Setters =
  307. {
  308. new Setter(
  309. TemplatedControl.TemplateProperty,
  310. new FuncControlTemplate((_, __) => new Decorator
  311. {
  312. Child = new Border(),
  313. }))
  314. }
  315. }
  316. },
  317. Child = target = new TestTemplatedControl()
  318. };
  319. Assert.NotNull(target.Template);
  320. target.ApplyTemplate();
  321. root.Child = null;
  322. Assert.Null(target.Template);
  323. Assert.IsType<Decorator>(target.GetVisualChildren().Single());
  324. }
  325. }
  326. [Fact]
  327. public void Re_adding_To_Same_LogicalTree_Should_Not_Recreate_Template()
  328. {
  329. using (UnitTestApplication.Start(TestServices.RealStyler))
  330. {
  331. TestTemplatedControl target;
  332. var root = new TestRoot
  333. {
  334. Styles =
  335. {
  336. new Style(x => x.OfType<TestTemplatedControl>())
  337. {
  338. Setters =
  339. {
  340. new Setter(
  341. TemplatedControl.TemplateProperty,
  342. new FuncControlTemplate((_, __) => new Decorator
  343. {
  344. Child = new Border(),
  345. }))
  346. }
  347. }
  348. },
  349. Child = target = new TestTemplatedControl()
  350. };
  351. Assert.NotNull(target.Template);
  352. target.ApplyTemplate();
  353. var expected = (Decorator)target.GetVisualChildren().Single();
  354. root.Child = null;
  355. root.Child = target;
  356. target.ApplyTemplate();
  357. Assert.Same(expected, target.GetVisualChildren().Single());
  358. }
  359. }
  360. [Fact]
  361. public void Re_adding_To_Different_LogicalTree_Should_Recreate_Template()
  362. {
  363. using (UnitTestApplication.Start(TestServices.RealStyler))
  364. {
  365. TestTemplatedControl target;
  366. var root = new TestRoot
  367. {
  368. Styles =
  369. {
  370. new Style(x => x.OfType<TestTemplatedControl>())
  371. {
  372. Setters =
  373. {
  374. new Setter(
  375. TemplatedControl.TemplateProperty,
  376. new FuncControlTemplate((_, __) => new Decorator
  377. {
  378. Child = new Border(),
  379. }))
  380. }
  381. }
  382. },
  383. Child = target = new TestTemplatedControl()
  384. };
  385. var root2 = new TestRoot
  386. {
  387. Styles =
  388. {
  389. new Style(x => x.OfType<TestTemplatedControl>())
  390. {
  391. Setters =
  392. {
  393. new Setter(
  394. TemplatedControl.TemplateProperty,
  395. new FuncControlTemplate((_, __) => new Decorator
  396. {
  397. Child = new Border(),
  398. }))
  399. }
  400. }
  401. },
  402. };
  403. Assert.NotNull(target.Template);
  404. target.ApplyTemplate();
  405. var expected = (Decorator)target.GetVisualChildren().Single();
  406. root.Child = null;
  407. root2.Child = target;
  408. target.ApplyTemplate();
  409. var child = target.GetVisualChildren().Single();
  410. Assert.NotNull(target.Template);
  411. Assert.NotNull(child);
  412. Assert.NotSame(expected, child);
  413. }
  414. }
  415. [Fact]
  416. public void Moving_To_New_LogicalTree_Should_Detach_Attach_Template_Child()
  417. {
  418. using (UnitTestApplication.Start(TestServices.RealStyler))
  419. {
  420. TestTemplatedControl target;
  421. var root = new TestRoot
  422. {
  423. Child = target = new TestTemplatedControl
  424. {
  425. Template = new FuncControlTemplate((_, __) => new Decorator()),
  426. }
  427. };
  428. Assert.NotNull(target.Template);
  429. target.ApplyTemplate();
  430. var templateChild = (ILogical)target.GetVisualChildren().Single();
  431. Assert.True(templateChild.IsAttachedToLogicalTree);
  432. root.Child = null;
  433. Assert.False(templateChild.IsAttachedToLogicalTree);
  434. var newRoot = new TestRoot { Child = target };
  435. Assert.True(templateChild.IsAttachedToLogicalTree);
  436. }
  437. }
  438. private static IControl ScrollingContentControlTemplate(ContentControl control, INameScope scope)
  439. {
  440. return new Border
  441. {
  442. Child = new ScrollViewer
  443. {
  444. Template = new FuncControlTemplate<ScrollViewer>(ScrollViewerTemplate),
  445. Name = "ScrollViewer",
  446. Content = new ContentPresenter
  447. {
  448. Name = "PART_ContentPresenter",
  449. [!ContentPresenter.ContentProperty] = control[!ContentControl.ContentProperty],
  450. }.RegisterInNameScope(scope)
  451. }.RegisterInNameScope(scope)
  452. };
  453. }
  454. private static Control ScrollViewerTemplate(ScrollViewer control, INameScope scope)
  455. {
  456. var result = new ScrollContentPresenter
  457. {
  458. Name = "PART_ContentPresenter",
  459. [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty],
  460. }.RegisterInNameScope(scope);
  461. return result;
  462. }
  463. }
  464. }