TreeViewTests.cs 10 KB

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