TreeViewTests.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. // Copyright (c) The Perspex 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.Generic;
  4. using System.Linq;
  5. using Perspex.Collections;
  6. using Perspex.Controls.Presenters;
  7. using Perspex.Controls.Templates;
  8. using Perspex.Input;
  9. using Perspex.LogicalTree;
  10. using Perspex.UnitTests;
  11. using Xunit;
  12. namespace Perspex.Controls.UnitTests
  13. {
  14. public class TreeViewTests
  15. {
  16. [Fact]
  17. public void Items_Should_Be_Created()
  18. {
  19. var target = new TreeView
  20. {
  21. Template = CreateTreeViewTemplate(),
  22. Items = CreateTestTreeData(),
  23. DataTemplates = CreateNodeDataTemplate(),
  24. };
  25. ApplyTemplates(target);
  26. Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
  27. Assert.Equal(new[] { "Child1", "Child2" }, ExtractItemHeader(target, 1));
  28. Assert.Equal(new[] { "Grandchild2a" }, ExtractItemHeader(target, 2));
  29. }
  30. [Fact]
  31. public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers()
  32. {
  33. var target = new TreeView
  34. {
  35. Template = CreateTreeViewTemplate(),
  36. Items = CreateTestTreeData(),
  37. DataTemplates = CreateNodeDataTemplate(),
  38. };
  39. ApplyTemplates(target);
  40. var container = (TreeViewItem)target.ItemContainerGenerator.Containers.Single().ContainerControl;
  41. var header = (TextBlock)container.Header;
  42. Assert.Equal("Root", header.Text);
  43. }
  44. [Fact]
  45. public void Root_TreeContainerFromItem_Should_Return_Descendent_Item()
  46. {
  47. var tree = CreateTestTreeData();
  48. var target = new TreeView
  49. {
  50. Template = CreateTreeViewTemplate(),
  51. Items = tree,
  52. DataTemplates = CreateNodeDataTemplate(),
  53. };
  54. // For TreeViewItem to find its parent TreeView, OnAttachedToLogicalTree needs
  55. // to be called, which requires an IStyleRoot.
  56. var root = new TestRoot();
  57. root.Child = target;
  58. ApplyTemplates(target);
  59. var container = target.ItemContainerGenerator.Index.ContainerFromItem(
  60. tree[0].Children[1].Children[0]);
  61. Assert.NotNull(container);
  62. var header = ((TreeViewItem)container).Header;
  63. var headerContent = ((TextBlock)header).Text;
  64. Assert.Equal("Grandchild2a", headerContent);
  65. }
  66. [Fact]
  67. public void Clicking_Item_Should_Select_It()
  68. {
  69. var tree = CreateTestTreeData();
  70. var target = new TreeView
  71. {
  72. Template = CreateTreeViewTemplate(),
  73. Items = tree,
  74. DataTemplates = CreateNodeDataTemplate(),
  75. };
  76. var visualRoot = new TestRoot();
  77. visualRoot.Child = target;
  78. ApplyTemplates(target);
  79. var item = tree[0].Children[1].Children[0];
  80. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  81. Assert.NotNull(container);
  82. container.RaiseEvent(new PointerPressedEventArgs
  83. {
  84. RoutedEvent = InputElement.PointerPressedEvent,
  85. MouseButton = MouseButton.Left,
  86. });
  87. Assert.Equal(item, target.SelectedItem);
  88. Assert.True(container.IsSelected);
  89. }
  90. [Fact]
  91. public void Setting_SelectedItem_Should_Set_Container_Selected()
  92. {
  93. var tree = CreateTestTreeData();
  94. var target = new TreeView
  95. {
  96. Template = CreateTreeViewTemplate(),
  97. Items = tree,
  98. DataTemplates = CreateNodeDataTemplate(),
  99. };
  100. var visualRoot = new TestRoot();
  101. visualRoot.Child = target;
  102. ApplyTemplates(target);
  103. var item = tree[0].Children[1].Children[0];
  104. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  105. Assert.NotNull(container);
  106. target.SelectedItem = item;
  107. Assert.True(container.IsSelected);
  108. }
  109. [Fact]
  110. public void LogicalChildren_Should_Be_Set()
  111. {
  112. var target = new TreeView
  113. {
  114. Template = CreateTreeViewTemplate(),
  115. Items = new[] { "Foo", "Bar", "Baz " },
  116. };
  117. ApplyTemplates(target);
  118. var result = target.GetLogicalChildren()
  119. .OfType<TreeViewItem>()
  120. .Select(x => x.Header)
  121. .OfType<TextBlock>()
  122. .Select(x => x.Text)
  123. .ToList();
  124. Assert.Equal(new[] { "Foo", "Bar", "Baz " }, result);
  125. }
  126. [Fact]
  127. public void Removing_Item_Should_Remove_Itself_And_Children_From_Index()
  128. {
  129. var tree = CreateTestTreeData();
  130. var target = new TreeView
  131. {
  132. Template = CreateTreeViewTemplate(),
  133. DataTemplates = CreateNodeDataTemplate(),
  134. Items = tree,
  135. };
  136. var root = new TestRoot();
  137. root.Child = target;
  138. ApplyTemplates(target);
  139. Assert.Equal(4, target.ItemContainerGenerator.Index.Items.Count());
  140. tree[0].Children.RemoveAt(1);
  141. Assert.Equal(2, target.ItemContainerGenerator.Index.Items.Count());
  142. }
  143. [Fact]
  144. public void DataContexts_Should_Be_Correctly_Set()
  145. {
  146. var items = new object[]
  147. {
  148. "Foo",
  149. new Node { Value = "Bar" },
  150. new TextBlock { Text = "Baz" },
  151. new TreeViewItem { Header = "Qux" },
  152. };
  153. var target = new TreeView
  154. {
  155. Template = CreateTreeViewTemplate(),
  156. DataContext = "Base",
  157. DataTemplates = new DataTemplates
  158. {
  159. new FuncDataTemplate<Node>(x => new Button { Content = x })
  160. },
  161. Items = items,
  162. };
  163. ApplyTemplates(target);
  164. var dataContexts = target.Presenter.Panel.Children
  165. .Cast<Control>()
  166. .Select(x => x.DataContext)
  167. .ToList();
  168. Assert.Equal(
  169. new object[] { items[0], items[1], "Base", "Base" },
  170. dataContexts);
  171. }
  172. private void ApplyTemplates(TreeView tree)
  173. {
  174. tree.ApplyTemplate();
  175. tree.Presenter.ApplyTemplate();
  176. ApplyTemplates(tree.Presenter.Panel.Children);
  177. }
  178. private void ApplyTemplates(IEnumerable<IControl> controls)
  179. {
  180. foreach (TreeViewItem control in controls)
  181. {
  182. control.Template = CreateTreeViewItemTemplate();
  183. control.ApplyTemplate();
  184. control.Presenter.ApplyTemplate();
  185. ApplyTemplates(control.Presenter.Panel.Children);
  186. }
  187. }
  188. private IList<Node> CreateTestTreeData()
  189. {
  190. return new PerspexList<Node>
  191. {
  192. new Node
  193. {
  194. Value = "Root",
  195. Children = new PerspexList<Node>
  196. {
  197. new Node
  198. {
  199. Value = "Child1",
  200. },
  201. new Node
  202. {
  203. Value = "Child2",
  204. Children = new PerspexList<Node>
  205. {
  206. new Node
  207. {
  208. Value = "Grandchild2a",
  209. },
  210. },
  211. },
  212. }
  213. }
  214. };
  215. }
  216. private DataTemplates CreateNodeDataTemplate()
  217. {
  218. return new DataTemplates
  219. {
  220. new FuncTreeDataTemplate<Node>(
  221. x => new TextBlock { Text = x.Value },
  222. x => x.Children),
  223. };
  224. }
  225. private IControlTemplate CreateTreeViewTemplate()
  226. {
  227. return new FuncControlTemplate<TreeView>(parent => new ItemsPresenter
  228. {
  229. Name = "PART_ItemsPresenter",
  230. [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
  231. });
  232. }
  233. private IControlTemplate CreateTreeViewItemTemplate()
  234. {
  235. return new FuncControlTemplate<TreeViewItem>(parent => new ItemsPresenter
  236. {
  237. Name = "PART_ItemsPresenter",
  238. [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
  239. });
  240. }
  241. private List<string> ExtractItemHeader(TreeView tree, int level)
  242. {
  243. return ExtractItemContent(tree.Presenter.Panel, 0, level)
  244. .Select(x => x.Header)
  245. .OfType<TextBlock>()
  246. .Select(x => x.Text)
  247. .ToList();
  248. }
  249. private IEnumerable<TreeViewItem> ExtractItemContent(IPanel panel, int currentLevel, int level)
  250. {
  251. foreach (TreeViewItem container in panel.Children)
  252. {
  253. if (container.Template == null)
  254. {
  255. container.Template = CreateTreeViewItemTemplate();
  256. container.ApplyTemplate();
  257. }
  258. if (currentLevel == level)
  259. {
  260. yield return container;
  261. }
  262. else
  263. {
  264. foreach (var child in ExtractItemContent(container.Presenter.Panel, currentLevel + 1, level))
  265. {
  266. yield return child;
  267. }
  268. }
  269. }
  270. }
  271. private class Node
  272. {
  273. public string Value { get; set; }
  274. public IPerspexList<Node> Children { get; set; }
  275. }
  276. }
  277. }