ItemsControlTests.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  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.Collections.Specialized;
  4. using System.Linq;
  5. using Avalonia.Collections;
  6. using Avalonia.Controls.Presenters;
  7. using Avalonia.Controls.Templates;
  8. using Avalonia.LogicalTree;
  9. using Avalonia.VisualTree;
  10. using Xunit;
  11. using System.Collections.ObjectModel;
  12. using Avalonia.UnitTests;
  13. namespace Avalonia.Controls.UnitTests
  14. {
  15. public class ItemsControlTests
  16. {
  17. [Fact]
  18. public void Should_Use_ItemTemplate_To_Create_Control()
  19. {
  20. var target = new ItemsControl
  21. {
  22. Template = GetTemplate(),
  23. ItemTemplate = new FuncDataTemplate<string>(_ => new Canvas()),
  24. };
  25. target.Items = new[] { "Foo" };
  26. target.ApplyTemplate();
  27. target.Presenter.ApplyTemplate();
  28. var container = (ContentPresenter)target.Presenter.Panel.Children[0];
  29. container.UpdateChild();
  30. Assert.IsType<Canvas>(container.Child);
  31. }
  32. [Fact]
  33. public void Panel_Should_Have_TemplatedParent_Set_To_ItemsControl()
  34. {
  35. var target = new ItemsControl();
  36. target.Template = GetTemplate();
  37. target.Items = new[] { "Foo" };
  38. target.ApplyTemplate();
  39. target.Presenter.ApplyTemplate();
  40. Assert.Equal(target, target.Presenter.Panel.TemplatedParent);
  41. }
  42. [Fact]
  43. public void Container_Should_Have_TemplatedParent_Set_To_Null()
  44. {
  45. var target = new ItemsControl();
  46. target.Template = GetTemplate();
  47. target.Items = new[] { "Foo" };
  48. target.ApplyTemplate();
  49. target.Presenter.ApplyTemplate();
  50. var container = (ContentPresenter)target.Presenter.Panel.Children[0];
  51. Assert.Null(container.TemplatedParent);
  52. }
  53. [Fact]
  54. public void Container_Child_Should_Have_LogicalParent_Set_To_Container()
  55. {
  56. using (UnitTestApplication.Start(TestServices.StyledWindow))
  57. {
  58. var root = new Window();
  59. var target = new ItemsControl();
  60. root.Content = target;
  61. var templatedParent = new Button();
  62. target.SetValue(StyledElement.TemplatedParentProperty, templatedParent);
  63. target.Template = GetTemplate();
  64. target.Items = new[] { "Foo" };
  65. root.ApplyTemplate();
  66. target.ApplyTemplate();
  67. target.Presenter.ApplyTemplate();
  68. var container = (ContentPresenter)target.Presenter.Panel.Children[0];
  69. Assert.Equal(container, container.Child.Parent);
  70. }
  71. }
  72. [Fact]
  73. public void Control_Item_Should_Be_Logical_Child_Before_ApplyTemplate()
  74. {
  75. var target = new ItemsControl();
  76. var child = new Control();
  77. target.Template = GetTemplate();
  78. target.Items = new[] { child };
  79. Assert.Equal(child.Parent, target);
  80. Assert.Equal(child.GetLogicalParent(), target);
  81. Assert.Equal(new[] { child }, target.GetLogicalChildren());
  82. }
  83. [Fact]
  84. public void Control_Item_Should_Be_Removed_From_Logical_Children_Before_ApplyTemplate()
  85. {
  86. var target = new ItemsControl();
  87. var child = new Control();
  88. var items = new AvaloniaList<Control>(child);
  89. target.Template = GetTemplate();
  90. target.Items = items;
  91. items.RemoveAt(0);
  92. Assert.Null(child.Parent);
  93. Assert.Null(child.GetLogicalParent());
  94. Assert.Empty(target.GetLogicalChildren());
  95. }
  96. [Fact]
  97. public void Clearing_Items_Should_Clear_Child_Controls_Parent_Before_ApplyTemplate()
  98. {
  99. var target = new ItemsControl();
  100. var child = new Control();
  101. target.Template = GetTemplate();
  102. target.Items = new[] { child };
  103. target.Items = null;
  104. Assert.Null(child.Parent);
  105. Assert.Null(((ILogical)child).LogicalParent);
  106. }
  107. [Fact]
  108. public void Clearing_Items_Should_Clear_Child_Controls_Parent()
  109. {
  110. var target = new ItemsControl();
  111. var child = new Control();
  112. target.Template = GetTemplate();
  113. target.Items = new[] { child };
  114. target.ApplyTemplate();
  115. target.Items = null;
  116. Assert.Null(child.Parent);
  117. Assert.Null(((ILogical)child).LogicalParent);
  118. }
  119. [Fact]
  120. public void Adding_Control_Item_Should_Make_Control_Appear_In_LogicalChildren()
  121. {
  122. var target = new ItemsControl();
  123. var child = new Control();
  124. target.Template = GetTemplate();
  125. target.Items = new[] { child };
  126. // Should appear both before and after applying template.
  127. Assert.Equal(new ILogical[] { child }, target.GetLogicalChildren());
  128. target.ApplyTemplate();
  129. Assert.Equal(new ILogical[] { child }, target.GetLogicalChildren());
  130. }
  131. [Fact]
  132. public void Adding_String_Item_Should_Make_TextBlock_Appear_In_LogicalChildren()
  133. {
  134. var target = new ItemsControl();
  135. var child = new Control();
  136. target.Template = GetTemplate();
  137. target.Items = new[] { "Foo" };
  138. target.ApplyTemplate();
  139. target.Presenter.ApplyTemplate();
  140. var logical = (ILogical)target;
  141. Assert.Equal(1, logical.LogicalChildren.Count);
  142. Assert.IsType<TextBlock>(logical.LogicalChildren[0]);
  143. }
  144. [Fact]
  145. public void Setting_Items_To_Null_Should_Remove_LogicalChildren()
  146. {
  147. var target = new ItemsControl();
  148. var child = new Control();
  149. target.Template = GetTemplate();
  150. target.Items = new[] { "Foo" };
  151. target.ApplyTemplate();
  152. target.Presenter.ApplyTemplate();
  153. Assert.NotEmpty(target.GetLogicalChildren());
  154. target.Items = null;
  155. Assert.Equal(new ILogical[0], target.GetLogicalChildren());
  156. }
  157. [Fact]
  158. public void Setting_Items_Should_Fire_LogicalChildren_CollectionChanged()
  159. {
  160. var target = new ItemsControl();
  161. var child = new Control();
  162. var called = false;
  163. target.Template = GetTemplate();
  164. target.ApplyTemplate();
  165. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  166. called = e.Action == NotifyCollectionChangedAction.Add;
  167. target.Items = new[] { child };
  168. Assert.True(called);
  169. }
  170. [Fact]
  171. public void Setting_Items_To_Null_Should_Fire_LogicalChildren_CollectionChanged()
  172. {
  173. var target = new ItemsControl();
  174. var child = new Control();
  175. var called = false;
  176. target.Template = GetTemplate();
  177. target.Items = new[] { child };
  178. target.ApplyTemplate();
  179. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  180. called = e.Action == NotifyCollectionChangedAction.Remove;
  181. target.Items = null;
  182. Assert.True(called);
  183. }
  184. [Fact]
  185. public void Changing_Items_Should_Fire_LogicalChildren_CollectionChanged()
  186. {
  187. var target = new ItemsControl();
  188. var child = new Control();
  189. var called = false;
  190. target.Template = GetTemplate();
  191. target.Items = new[] { child };
  192. target.ApplyTemplate();
  193. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) => called = true;
  194. target.Items = new[] { "Foo" };
  195. Assert.True(called);
  196. }
  197. [Fact]
  198. public void Adding_Items_Should_Fire_LogicalChildren_CollectionChanged()
  199. {
  200. var target = new ItemsControl();
  201. var items = new AvaloniaList<string> { "Foo" };
  202. var called = false;
  203. target.Template = GetTemplate();
  204. target.Items = items;
  205. target.ApplyTemplate();
  206. target.Presenter.ApplyTemplate();
  207. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  208. called = e.Action == NotifyCollectionChangedAction.Add;
  209. items.Add("Bar");
  210. Assert.True(called);
  211. }
  212. [Fact]
  213. public void Removing_Items_Should_Fire_LogicalChildren_CollectionChanged()
  214. {
  215. var target = new ItemsControl();
  216. var items = new AvaloniaList<string> { "Foo", "Bar" };
  217. var called = false;
  218. target.Template = GetTemplate();
  219. target.Items = items;
  220. target.ApplyTemplate();
  221. target.Presenter.ApplyTemplate();
  222. ((ILogical)target).LogicalChildren.CollectionChanged += (s, e) =>
  223. called = e.Action == NotifyCollectionChangedAction.Remove;
  224. items.Remove("Bar");
  225. Assert.True(called);
  226. }
  227. [Fact]
  228. public void LogicalChildren_Should_Not_Change_Instance_When_Template_Changed()
  229. {
  230. var target = new ItemsControl()
  231. {
  232. Template = GetTemplate(),
  233. };
  234. var before = ((ILogical)target).LogicalChildren;
  235. target.Template = null;
  236. target.Template = GetTemplate();
  237. var after = ((ILogical)target).LogicalChildren;
  238. Assert.NotNull(before);
  239. Assert.NotNull(after);
  240. Assert.Same(before, after);
  241. }
  242. [Fact]
  243. public void Empty_Class_Should_Initially_Be_Applied()
  244. {
  245. var target = new ItemsControl()
  246. {
  247. Template = GetTemplate(),
  248. };
  249. Assert.Contains(":empty", target.Classes);
  250. }
  251. [Fact]
  252. public void Empty_Class_Should_Be_Cleared_When_Items_Added()
  253. {
  254. var target = new ItemsControl()
  255. {
  256. Template = GetTemplate(),
  257. Items = new[] { 1, 2, 3 },
  258. };
  259. Assert.DoesNotContain(":empty", target.Classes);
  260. }
  261. [Fact]
  262. public void Empty_Class_Should_Be_Set_When_Empty_Collection_Set()
  263. {
  264. var target = new ItemsControl()
  265. {
  266. Template = GetTemplate(),
  267. Items = new[] { 1, 2, 3 },
  268. };
  269. target.Items = new int[0];
  270. Assert.Contains(":empty", target.Classes);
  271. }
  272. [Fact]
  273. public void Setting_Presenter_Explicitly_Should_Set_Item_Parent()
  274. {
  275. var target = new TestItemsControl();
  276. var child = new Control();
  277. var presenter = new ItemsPresenter
  278. {
  279. [StyledElement.TemplatedParentProperty] = target,
  280. [~ItemsPresenter.ItemsProperty] = target[~ItemsControl.ItemsProperty],
  281. };
  282. presenter.ApplyTemplate();
  283. target.Presenter = presenter;
  284. target.Items = new[] { child };
  285. target.ApplyTemplate();
  286. Assert.Equal(target, child.Parent);
  287. Assert.Equal(target, ((ILogical)child).LogicalParent);
  288. }
  289. [Fact]
  290. public void DataContexts_Should_Be_Correctly_Set()
  291. {
  292. var items = new object[]
  293. {
  294. "Foo",
  295. new Item("Bar"),
  296. new TextBlock { Text = "Baz" },
  297. new ListBoxItem { Content = "Qux" },
  298. };
  299. var target = new ItemsControl
  300. {
  301. Template = GetTemplate(),
  302. DataContext = "Base",
  303. DataTemplates =
  304. {
  305. new FuncDataTemplate<Item>(x => new Button { Content = x })
  306. },
  307. Items = items,
  308. };
  309. target.ApplyTemplate();
  310. target.Presenter.ApplyTemplate();
  311. var dataContexts = target.Presenter.Panel.Children
  312. .Do(x => (x as ContentPresenter)?.UpdateChild())
  313. .Cast<Control>()
  314. .Select(x => x.DataContext)
  315. .ToList();
  316. Assert.Equal(
  317. new object[] { items[0], items[1], "Base", "Base" },
  318. dataContexts);
  319. }
  320. [Fact]
  321. public void MemberSelector_Should_Select_Member()
  322. {
  323. var target = new ItemsControl
  324. {
  325. Template = GetTemplate(),
  326. Items = new[] { new Item("Foo"), new Item("Bar") },
  327. MemberSelector = new FuncMemberSelector<Item, string>(x => x.Value),
  328. };
  329. target.ApplyTemplate();
  330. target.Presenter.ApplyTemplate();
  331. var text = target.Presenter.Panel.Children
  332. .Cast<ContentPresenter>()
  333. .Select(x => x.Content)
  334. .ToList();
  335. Assert.Equal(new[] { "Foo", "Bar" }, text);
  336. }
  337. [Fact]
  338. public void Control_Item_Should_Not_Be_NameScope()
  339. {
  340. var items = new object[]
  341. {
  342. new TextBlock(),
  343. };
  344. var target = new ItemsControl
  345. {
  346. Template = GetTemplate(),
  347. Items = items,
  348. };
  349. target.ApplyTemplate();
  350. target.Presenter.ApplyTemplate();
  351. var item = target.Presenter.Panel.LogicalChildren[0];
  352. Assert.Null(NameScope.GetNameScope((TextBlock)item));
  353. }
  354. [Fact]
  355. public void DataTemplate_Created_Content_Should_Be_NameScope()
  356. {
  357. var items = new object[]
  358. {
  359. "foo",
  360. };
  361. var target = new ItemsControl
  362. {
  363. Template = GetTemplate(),
  364. Items = items,
  365. };
  366. target.ApplyTemplate();
  367. target.Presenter.ApplyTemplate();
  368. var container = (ContentPresenter)target.Presenter.Panel.LogicalChildren[0];
  369. container.UpdateChild();
  370. Assert.NotNull(NameScope.GetNameScope((TextBlock)container.Child));
  371. }
  372. private class Item
  373. {
  374. public Item(string value)
  375. {
  376. Value = value;
  377. }
  378. public string Value { get; }
  379. }
  380. private FuncControlTemplate GetTemplate()
  381. {
  382. return new FuncControlTemplate<ItemsControl>(parent =>
  383. {
  384. return new Border
  385. {
  386. Background = new Media.SolidColorBrush(0xffffffff),
  387. Child = new ItemsPresenter
  388. {
  389. Name = "PART_ItemsPresenter",
  390. MemberSelector = parent.MemberSelector,
  391. [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
  392. }
  393. };
  394. });
  395. }
  396. private class TestItemsControl : ItemsControl
  397. {
  398. public new IItemsPresenter Presenter
  399. {
  400. get { return base.Presenter; }
  401. set { base.Presenter = value; }
  402. }
  403. }
  404. }
  405. }