TemplatedControlTests.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  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_Child_Should_Be_NameScope()
  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. Assert.NotNull(NameScope.GetNameScope((Control)target.GetVisualChildren().Single()));
  99. }
  100. [Fact]
  101. public void Templated_Children_Should_Have_TemplatedParent_Set()
  102. {
  103. var target = new TemplatedControl
  104. {
  105. Template = new FuncControlTemplate(_ => new Decorator
  106. {
  107. Child = new Panel
  108. {
  109. Children =
  110. {
  111. new TextBlock(),
  112. new Border(),
  113. }
  114. }
  115. }),
  116. };
  117. target.ApplyTemplate();
  118. var templatedParents = target.GetVisualDescendants()
  119. .OfType<IControl>()
  120. .Select(x => x.TemplatedParent)
  121. .ToList();
  122. Assert.Equal(4, templatedParents.Count);
  123. Assert.True(templatedParents.All(x => x == target));
  124. }
  125. [Fact]
  126. public void Templated_Child_Should_Have_Parent_Set()
  127. {
  128. var target = new TemplatedControl
  129. {
  130. Template = new FuncControlTemplate(_ => new Decorator())
  131. };
  132. target.ApplyTemplate();
  133. var child = (Decorator)target.GetVisualChildren().Single();
  134. Assert.Equal(target, child.Parent);
  135. Assert.Equal(target, child.GetLogicalParent());
  136. }
  137. [Fact]
  138. public void Nested_Templated_Control_Should_Not_Have_Template_Applied()
  139. {
  140. var target = new TemplatedControl
  141. {
  142. Template = new FuncControlTemplate(_ => new ScrollViewer())
  143. };
  144. target.ApplyTemplate();
  145. var child = (ScrollViewer)target.GetVisualChildren().Single();
  146. Assert.Empty(child.GetVisualChildren());
  147. }
  148. [Fact]
  149. public void Templated_Children_Should_Be_Styled()
  150. {
  151. using (UnitTestApplication.Start(TestServices.MockStyler))
  152. {
  153. TestTemplatedControl target;
  154. var root = new TestRoot
  155. {
  156. Child = target = new TestTemplatedControl
  157. {
  158. Template = new FuncControlTemplate(_ =>
  159. {
  160. return new StackPanel
  161. {
  162. Children =
  163. {
  164. new TextBlock
  165. {
  166. }
  167. }
  168. };
  169. }),
  170. }
  171. };
  172. target.ApplyTemplate();
  173. var styler = Mock.Get(UnitTestApplication.Current.Services.Styler);
  174. styler.Verify(x => x.ApplyStyles(It.IsAny<TestTemplatedControl>()), Times.Once());
  175. styler.Verify(x => x.ApplyStyles(It.IsAny<StackPanel>()), Times.Once());
  176. styler.Verify(x => x.ApplyStyles(It.IsAny<TextBlock>()), Times.Once());
  177. }
  178. }
  179. [Fact]
  180. public void Nested_Templated_Controls_Have_Correct_TemplatedParent()
  181. {
  182. var target = new TestTemplatedControl
  183. {
  184. Template = new FuncControlTemplate(_ =>
  185. {
  186. return new ContentControl
  187. {
  188. Template = new FuncControlTemplate(parent =>
  189. {
  190. return new Border
  191. {
  192. Child = new ContentPresenter
  193. {
  194. [~ContentPresenter.ContentProperty] = parent.GetObservable(ContentControl.ContentProperty).ToBinding(),
  195. }
  196. };
  197. }),
  198. Content = new Decorator
  199. {
  200. Child = new TextBlock()
  201. }
  202. };
  203. }),
  204. };
  205. target.ApplyTemplate();
  206. var contentControl = target.GetTemplateChildren().OfType<ContentControl>().Single();
  207. contentControl.ApplyTemplate();
  208. var border = contentControl.GetTemplateChildren().OfType<Border>().Single();
  209. var presenter = contentControl.GetTemplateChildren().OfType<ContentPresenter>().Single();
  210. var decorator = (Decorator)presenter.Content;
  211. var textBlock = (TextBlock)decorator.Child;
  212. Assert.Equal(target, contentControl.TemplatedParent);
  213. Assert.Equal(contentControl, border.TemplatedParent);
  214. Assert.Equal(contentControl, presenter.TemplatedParent);
  215. Assert.Equal(target, decorator.TemplatedParent);
  216. Assert.Equal(target, textBlock.TemplatedParent);
  217. }
  218. [Fact]
  219. public void Nested_TemplatedControls_Should_Register_With_Correct_NameScope()
  220. {
  221. var target = new ContentControl
  222. {
  223. Template = new FuncControlTemplate<ContentControl>(ScrollingContentControlTemplate),
  224. Content = "foo"
  225. };
  226. var root = new TestRoot { Child = target };
  227. target.ApplyTemplate();
  228. var border = target.GetVisualChildren().FirstOrDefault();
  229. Assert.IsType<Border>(border);
  230. var scrollViewer = border.GetVisualChildren().FirstOrDefault();
  231. Assert.IsType<ScrollViewer>(scrollViewer);
  232. ((ScrollViewer)scrollViewer).ApplyTemplate();
  233. var scrollContentPresenter = scrollViewer.GetVisualChildren().FirstOrDefault();
  234. Assert.IsType<ScrollContentPresenter>(scrollContentPresenter);
  235. ((ContentPresenter)scrollContentPresenter).UpdateChild();
  236. var contentPresenter = scrollContentPresenter.GetVisualChildren().FirstOrDefault();
  237. Assert.IsType<ContentPresenter>(contentPresenter);
  238. var borderNs = NameScope.GetNameScope((Control)border);
  239. var scrollContentPresenterNs = NameScope.GetNameScope((Control)scrollContentPresenter);
  240. Assert.NotNull(borderNs);
  241. Assert.Same(scrollViewer, borderNs.Find("ScrollViewer"));
  242. Assert.Same(contentPresenter, borderNs.Find("PART_ContentPresenter"));
  243. Assert.Same(scrollContentPresenter, scrollContentPresenterNs.Find("PART_ContentPresenter"));
  244. }
  245. [Fact]
  246. public void ApplyTemplate_Should_Raise_TemplateApplied()
  247. {
  248. var target = new TestTemplatedControl
  249. {
  250. Template = new FuncControlTemplate(_ => new Decorator())
  251. };
  252. var raised = false;
  253. target.TemplateApplied += (s, e) =>
  254. {
  255. Assert.Equal(TemplatedControl.TemplateAppliedEvent, e.RoutedEvent);
  256. Assert.Same(target, e.Source);
  257. Assert.NotNull(e.NameScope);
  258. raised = true;
  259. };
  260. target.ApplyTemplate();
  261. Assert.True(raised);
  262. }
  263. [Fact]
  264. public void Applying_New_Template_Clears_TemplatedParent_Of_Old_Template_Children()
  265. {
  266. var target = new TestTemplatedControl
  267. {
  268. Template = new FuncControlTemplate(_ => new Decorator
  269. {
  270. Child = new Border(),
  271. })
  272. };
  273. target.ApplyTemplate();
  274. var decorator = (Decorator)target.GetVisualChildren().Single();
  275. var border = (Border)decorator.Child;
  276. Assert.Equal(target, decorator.TemplatedParent);
  277. Assert.Equal(target, border.TemplatedParent);
  278. target.Template = new FuncControlTemplate(_ => new Canvas());
  279. // Templated children should not be removed here: the control may be re-added
  280. // somewhere with the same template, so they could still be of use.
  281. Assert.Same(decorator, target.GetVisualChildren().Single());
  282. Assert.Equal(target, decorator.TemplatedParent);
  283. Assert.Equal(target, border.TemplatedParent);
  284. target.ApplyTemplate();
  285. Assert.Null(decorator.TemplatedParent);
  286. Assert.Null(border.TemplatedParent);
  287. }
  288. [Fact]
  289. public void TemplateChild_AttachedToLogicalTree_Should_Be_Raised()
  290. {
  291. Border templateChild = new Border();
  292. var root = new TestRoot
  293. {
  294. Child = new TestTemplatedControl
  295. {
  296. Template = new FuncControlTemplate(_ => new Decorator
  297. {
  298. Child = templateChild,
  299. })
  300. }
  301. };
  302. var raised = false;
  303. templateChild.AttachedToLogicalTree += (s, e) => raised = true;
  304. root.Child.ApplyTemplate();
  305. Assert.True(raised);
  306. }
  307. [Fact]
  308. public void TemplateChild_DetachedFromLogicalTree_Should_Be_Raised()
  309. {
  310. Border templateChild = new Border();
  311. var root = new TestRoot
  312. {
  313. Child = new TestTemplatedControl
  314. {
  315. Template = new FuncControlTemplate(_ => new Decorator
  316. {
  317. Child = templateChild,
  318. })
  319. }
  320. };
  321. root.Child.ApplyTemplate();
  322. var raised = false;
  323. templateChild.DetachedFromLogicalTree += (s, e) => raised = true;
  324. root.Child = null;
  325. Assert.True(raised);
  326. }
  327. [Fact]
  328. public void Removing_From_LogicalTree_Should_Not_Remove_Child()
  329. {
  330. using (UnitTestApplication.Start(TestServices.RealStyler))
  331. {
  332. Border templateChild = new Border();
  333. TestTemplatedControl target;
  334. var root = new TestRoot
  335. {
  336. Styles =
  337. {
  338. new Style(x => x.OfType<TestTemplatedControl>())
  339. {
  340. Setters = new[]
  341. {
  342. new Setter(
  343. TemplatedControl.TemplateProperty,
  344. new FuncControlTemplate(_ => new Decorator
  345. {
  346. Child = new Border(),
  347. }))
  348. }
  349. }
  350. },
  351. Child = target = new TestTemplatedControl()
  352. };
  353. Assert.NotNull(target.Template);
  354. target.ApplyTemplate();
  355. root.Child = null;
  356. Assert.Null(target.Template);
  357. Assert.IsType<Decorator>(target.GetVisualChildren().Single());
  358. }
  359. }
  360. [Fact]
  361. public void Re_adding_To_Same_LogicalTree_Should_Not_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 = new[]
  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. Assert.NotNull(target.Template);
  386. target.ApplyTemplate();
  387. var expected = (Decorator)target.GetVisualChildren().Single();
  388. root.Child = null;
  389. root.Child = target;
  390. target.ApplyTemplate();
  391. Assert.Same(expected, target.GetVisualChildren().Single());
  392. }
  393. }
  394. [Fact]
  395. public void Re_adding_To_Different_LogicalTree_Should_Recreate_Template()
  396. {
  397. using (UnitTestApplication.Start(TestServices.RealStyler))
  398. {
  399. TestTemplatedControl target;
  400. var root = new TestRoot
  401. {
  402. Styles =
  403. {
  404. new Style(x => x.OfType<TestTemplatedControl>())
  405. {
  406. Setters = new[]
  407. {
  408. new Setter(
  409. TemplatedControl.TemplateProperty,
  410. new FuncControlTemplate(_ => new Decorator
  411. {
  412. Child = new Border(),
  413. }))
  414. }
  415. }
  416. },
  417. Child = target = new TestTemplatedControl()
  418. };
  419. var root2 = new TestRoot
  420. {
  421. Styles =
  422. {
  423. new Style(x => x.OfType<TestTemplatedControl>())
  424. {
  425. Setters = new[]
  426. {
  427. new Setter(
  428. TemplatedControl.TemplateProperty,
  429. new FuncControlTemplate(_ => new Decorator
  430. {
  431. Child = new Border(),
  432. }))
  433. }
  434. }
  435. },
  436. };
  437. Assert.NotNull(target.Template);
  438. target.ApplyTemplate();
  439. var expected = (Decorator)target.GetVisualChildren().Single();
  440. root.Child = null;
  441. root2.Child = target;
  442. target.ApplyTemplate();
  443. var child = target.GetVisualChildren().Single();
  444. Assert.NotNull(target.Template);
  445. Assert.NotNull(child);
  446. Assert.NotSame(expected, child);
  447. }
  448. }
  449. [Fact]
  450. public void Moving_To_New_LogicalTree_Should_Detach_Attach_Template_Child()
  451. {
  452. using (UnitTestApplication.Start(TestServices.RealStyler))
  453. {
  454. TestTemplatedControl target;
  455. var root = new TestRoot
  456. {
  457. Child = target = new TestTemplatedControl
  458. {
  459. Template = new FuncControlTemplate(_ => new Decorator()),
  460. }
  461. };
  462. Assert.NotNull(target.Template);
  463. target.ApplyTemplate();
  464. var templateChild = (ILogical)target.GetVisualChildren().Single();
  465. Assert.True(templateChild.IsAttachedToLogicalTree);
  466. root.Child = null;
  467. Assert.False(templateChild.IsAttachedToLogicalTree);
  468. var newRoot = new TestRoot { Child = target };
  469. Assert.True(templateChild.IsAttachedToLogicalTree);
  470. }
  471. }
  472. private static IControl ScrollingContentControlTemplate(ContentControl control)
  473. {
  474. return new Border
  475. {
  476. Child = new ScrollViewer
  477. {
  478. Template = new FuncControlTemplate<ScrollViewer>(ScrollViewerTemplate),
  479. Name = "ScrollViewer",
  480. Content = new ContentPresenter
  481. {
  482. Name = "PART_ContentPresenter",
  483. [!ContentPresenter.ContentProperty] = control[!ContentControl.ContentProperty],
  484. }
  485. }
  486. };
  487. }
  488. private static Control ScrollViewerTemplate(ScrollViewer control)
  489. {
  490. var result = new ScrollContentPresenter
  491. {
  492. Name = "PART_ContentPresenter",
  493. [~ContentPresenter.ContentProperty] = control[~ContentControl.ContentProperty],
  494. };
  495. return result;
  496. }
  497. }
  498. }