ItemsControlTests.cs 14 KB

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