TemplatedControlTests.cs 18 KB

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