TemplatedControlTests.cs 20 KB

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