TemplatedControlTests.cs 20 KB

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