TreeViewTests.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  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;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using Avalonia.Collections;
  7. using Avalonia.Controls.Presenters;
  8. using Avalonia.Controls.Templates;
  9. using Avalonia.Data;
  10. using Avalonia.Data.Core;
  11. using Avalonia.Input;
  12. using Avalonia.Input.Platform;
  13. using Avalonia.Interactivity;
  14. using Avalonia.LogicalTree;
  15. using Avalonia.UnitTests;
  16. using Xunit;
  17. namespace Avalonia.Controls.UnitTests
  18. {
  19. public class TreeViewTests
  20. {
  21. MouseTestHelper _mouse = new MouseTestHelper();
  22. [Fact]
  23. public void Items_Should_Be_Created()
  24. {
  25. var target = new TreeView
  26. {
  27. Template = CreateTreeViewTemplate(),
  28. Items = CreateTestTreeData(),
  29. };
  30. CreateNodeDataTemplate(target);
  31. ApplyTemplates(target);
  32. Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
  33. Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1));
  34. Assert.Equal(new[] { "Grandchild2a" }, ExtractItemHeader(target, 2));
  35. }
  36. [Fact]
  37. public void Items_Should_Be_Created_Using_ItemTemplate_If_Present()
  38. {
  39. TreeView target;
  40. var root = new TestRoot
  41. {
  42. Child = target = new TreeView
  43. {
  44. Template = CreateTreeViewTemplate(),
  45. Items = CreateTestTreeData(),
  46. ItemTemplate = new FuncTreeDataTemplate<Node>(
  47. (_, __) => new Canvas(),
  48. x => x.Children),
  49. }
  50. };
  51. ApplyTemplates(target);
  52. var items = target.ItemContainerGenerator.Index.Containers
  53. .OfType<TreeViewItem>()
  54. .ToList();
  55. Assert.Equal(5, items.Count);
  56. Assert.All(items, x => Assert.IsType<Canvas>(x.HeaderPresenter.Child));
  57. }
  58. [Fact]
  59. public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers()
  60. {
  61. var target = new TreeView
  62. {
  63. Template = CreateTreeViewTemplate(),
  64. Items = CreateTestTreeData(),
  65. };
  66. CreateNodeDataTemplate(target);
  67. ApplyTemplates(target);
  68. var container = (TreeViewItem)target.ItemContainerGenerator.Containers.Single().ContainerControl;
  69. var header = (TextBlock)container.Header;
  70. Assert.Equal("Root", header.Text);
  71. }
  72. [Fact]
  73. public void Root_TreeContainerFromItem_Should_Return_Descendant_Item()
  74. {
  75. var tree = CreateTestTreeData();
  76. var target = new TreeView
  77. {
  78. Template = CreateTreeViewTemplate(),
  79. Items = tree,
  80. };
  81. // For TreeViewItem to find its parent TreeView, OnAttachedToLogicalTree needs
  82. // to be called, which requires an IStyleRoot.
  83. var root = new TestRoot();
  84. root.Child = target;
  85. CreateNodeDataTemplate(target);
  86. ApplyTemplates(target);
  87. var container = target.ItemContainerGenerator.Index.ContainerFromItem(
  88. tree[0].Children[1].Children[0]);
  89. Assert.NotNull(container);
  90. var header = ((TreeViewItem)container).Header;
  91. var headerContent = ((TextBlock)header).Text;
  92. Assert.Equal("Grandchild2a", headerContent);
  93. }
  94. [Fact]
  95. public void Clicking_Item_Should_Select_It()
  96. {
  97. var tree = CreateTestTreeData();
  98. var target = new TreeView
  99. {
  100. Template = CreateTreeViewTemplate(),
  101. Items = tree,
  102. };
  103. var visualRoot = new TestRoot();
  104. visualRoot.Child = target;
  105. CreateNodeDataTemplate(target);
  106. ApplyTemplates(target);
  107. var item = tree[0].Children[1].Children[0];
  108. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  109. Assert.NotNull(container);
  110. _mouse.Click(container);
  111. Assert.Equal(item, target.SelectedItem);
  112. Assert.True(container.IsSelected);
  113. }
  114. [Fact]
  115. public void Clicking_WithControlModifier_Selected_Item_Should_Deselect_It()
  116. {
  117. var tree = CreateTestTreeData();
  118. var target = new TreeView
  119. {
  120. Template = CreateTreeViewTemplate(),
  121. Items = tree
  122. };
  123. var visualRoot = new TestRoot();
  124. visualRoot.Child = target;
  125. CreateNodeDataTemplate(target);
  126. ApplyTemplates(target);
  127. var item = tree[0].Children[1].Children[0];
  128. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  129. Assert.NotNull(container);
  130. target.SelectedItem = item;
  131. Assert.True(container.IsSelected);
  132. _mouse.Click(container, modifiers: InputModifiers.Control);
  133. Assert.Null(target.SelectedItem);
  134. Assert.False(container.IsSelected);
  135. }
  136. [Fact]
  137. public void Clicking_WithControlModifier_Not_Selected_Item_Should_Select_It()
  138. {
  139. var tree = CreateTestTreeData();
  140. var target = new TreeView
  141. {
  142. Template = CreateTreeViewTemplate(),
  143. Items = tree
  144. };
  145. var visualRoot = new TestRoot();
  146. visualRoot.Child = target;
  147. CreateNodeDataTemplate(target);
  148. ApplyTemplates(target);
  149. var item1 = tree[0].Children[1].Children[0];
  150. var container1 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
  151. var item2 = tree[0].Children[1];
  152. var container2 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2);
  153. Assert.NotNull(container1);
  154. Assert.NotNull(container2);
  155. target.SelectedItem = item1;
  156. Assert.True(container1.IsSelected);
  157. _mouse.Click(container2, modifiers: InputModifiers.Control);
  158. Assert.Equal(item2, target.SelectedItem);
  159. Assert.False(container1.IsSelected);
  160. Assert.True(container2.IsSelected);
  161. }
  162. [Fact]
  163. public void Clicking_WithControlModifier_Selected_Item_Should_Deselect_And_Remove_From_SelectedItems()
  164. {
  165. var tree = CreateTestTreeData();
  166. var target = new TreeView
  167. {
  168. Template = CreateTreeViewTemplate(),
  169. Items = tree,
  170. SelectionMode = SelectionMode.Multiple
  171. };
  172. var visualRoot = new TestRoot();
  173. visualRoot.Child = target;
  174. CreateNodeDataTemplate(target);
  175. ApplyTemplates(target);
  176. var rootNode = tree[0];
  177. var item1 = rootNode.Children[0];
  178. var item2 = rootNode.Children.Last();
  179. var item1Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
  180. var item2Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2);
  181. ClickContainer(item1Container, InputModifiers.Control);
  182. Assert.True(item1Container.IsSelected);
  183. ClickContainer(item2Container, InputModifiers.Control);
  184. Assert.True(item2Container.IsSelected);
  185. Assert.Equal(new[] {item1, item2}, target.SelectedItems.OfType<Node>());
  186. ClickContainer(item1Container, InputModifiers.Control);
  187. Assert.False(item1Container.IsSelected);
  188. Assert.DoesNotContain(item1, target.SelectedItems.OfType<Node>());
  189. }
  190. [Fact]
  191. public void Clicking_WithShiftModifier_DownDirection_Should_Select_Range_Of_Items()
  192. {
  193. var tree = CreateTestTreeData();
  194. var target = new TreeView
  195. {
  196. Template = CreateTreeViewTemplate(),
  197. Items = tree,
  198. SelectionMode = SelectionMode.Multiple
  199. };
  200. var visualRoot = new TestRoot();
  201. visualRoot.Child = target;
  202. CreateNodeDataTemplate(target);
  203. ApplyTemplates(target);
  204. var rootNode = tree[0];
  205. var from = rootNode.Children[0];
  206. var to = rootNode.Children.Last();
  207. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  208. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  209. ClickContainer(fromContainer, InputModifiers.None);
  210. Assert.True(fromContainer.IsSelected);
  211. ClickContainer(toContainer, InputModifiers.Shift);
  212. AssertChildrenSelected(target, rootNode);
  213. }
  214. [Fact]
  215. public void Clicking_WithShiftModifier_UpDirection_Should_Select_Range_Of_Items()
  216. {
  217. var tree = CreateTestTreeData();
  218. var target = new TreeView
  219. {
  220. Template = CreateTreeViewTemplate(),
  221. Items = tree,
  222. SelectionMode = SelectionMode.Multiple
  223. };
  224. var visualRoot = new TestRoot();
  225. visualRoot.Child = target;
  226. CreateNodeDataTemplate(target);
  227. ApplyTemplates(target);
  228. var rootNode = tree[0];
  229. var from = rootNode.Children.Last();
  230. var to = rootNode.Children[0];
  231. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  232. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  233. ClickContainer(fromContainer, InputModifiers.None);
  234. Assert.True(fromContainer.IsSelected);
  235. ClickContainer(toContainer, InputModifiers.Shift);
  236. AssertChildrenSelected(target, rootNode);
  237. }
  238. [Fact]
  239. public void Clicking_First_Item_Of_SelectedItems_Should_Select_Only_It()
  240. {
  241. var tree = CreateTestTreeData();
  242. var target = new TreeView
  243. {
  244. Template = CreateTreeViewTemplate(),
  245. Items = tree,
  246. SelectionMode = SelectionMode.Multiple
  247. };
  248. var visualRoot = new TestRoot();
  249. visualRoot.Child = target;
  250. CreateNodeDataTemplate(target);
  251. ApplyTemplates(target);
  252. var rootNode = tree[0];
  253. var from = rootNode.Children.Last();
  254. var to = rootNode.Children[0];
  255. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  256. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  257. ClickContainer(fromContainer, InputModifiers.None);
  258. ClickContainer(toContainer, InputModifiers.Shift);
  259. AssertChildrenSelected(target, rootNode);
  260. ClickContainer(fromContainer, InputModifiers.None);
  261. Assert.True(fromContainer.IsSelected);
  262. foreach (var child in rootNode.Children)
  263. {
  264. if (child == from)
  265. {
  266. continue;
  267. }
  268. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(child);
  269. Assert.False(container.IsSelected);
  270. }
  271. }
  272. [Fact]
  273. public void Setting_SelectedItem_Should_Set_Container_Selected()
  274. {
  275. var tree = CreateTestTreeData();
  276. var target = new TreeView
  277. {
  278. Template = CreateTreeViewTemplate(),
  279. Items = tree,
  280. };
  281. var visualRoot = new TestRoot();
  282. visualRoot.Child = target;
  283. CreateNodeDataTemplate(target);
  284. ApplyTemplates(target);
  285. var item = tree[0].Children[1].Children[0];
  286. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  287. Assert.NotNull(container);
  288. target.SelectedItem = item;
  289. Assert.True(container.IsSelected);
  290. }
  291. [Fact]
  292. public void Setting_SelectedItem_Should_Raise_SelectedItemChanged_Event()
  293. {
  294. var tree = CreateTestTreeData();
  295. var target = new TreeView
  296. {
  297. Template = CreateTreeViewTemplate(),
  298. Items = tree,
  299. };
  300. var visualRoot = new TestRoot();
  301. visualRoot.Child = target;
  302. CreateNodeDataTemplate(target);
  303. ApplyTemplates(target);
  304. var item = tree[0].Children[1].Children[0];
  305. var called = false;
  306. target.SelectionChanged += (s, e) =>
  307. {
  308. Assert.Empty(e.RemovedItems);
  309. Assert.Equal(1, e.AddedItems.Count);
  310. Assert.Same(item, e.AddedItems[0]);
  311. called = true;
  312. };
  313. target.SelectedItem = item;
  314. Assert.True(called);
  315. }
  316. [Fact]
  317. public void LogicalChildren_Should_Be_Set()
  318. {
  319. var target = new TreeView
  320. {
  321. Template = CreateTreeViewTemplate(),
  322. Items = new[] { "Foo", "Bar", "Baz " },
  323. };
  324. ApplyTemplates(target);
  325. var result = target.GetLogicalChildren()
  326. .OfType<TreeViewItem>()
  327. .Select(x => x.Header)
  328. .OfType<TextBlock>()
  329. .Select(x => x.Text)
  330. .ToList();
  331. Assert.Equal(new[] { "Foo", "Bar", "Baz " }, result);
  332. }
  333. [Fact]
  334. public void Removing_Item_Should_Remove_Itself_And_Children_From_Index()
  335. {
  336. var tree = CreateTestTreeData();
  337. var target = new TreeView
  338. {
  339. Template = CreateTreeViewTemplate(),
  340. Items = tree,
  341. };
  342. var root = new TestRoot();
  343. root.Child = target;
  344. CreateNodeDataTemplate(target);
  345. ApplyTemplates(target);
  346. Assert.Equal(5, target.ItemContainerGenerator.Index.Containers.Count());
  347. tree[0].Children.RemoveAt(1);
  348. Assert.Equal(3, target.ItemContainerGenerator.Index.Containers.Count());
  349. }
  350. [Fact]
  351. public void DataContexts_Should_Be_Correctly_Set()
  352. {
  353. var items = new object[]
  354. {
  355. "Foo",
  356. new Node { Value = "Bar" },
  357. new TextBlock { Text = "Baz" },
  358. new TreeViewItem { Header = "Qux" },
  359. };
  360. var target = new TreeView
  361. {
  362. Template = CreateTreeViewTemplate(),
  363. DataContext = "Base",
  364. DataTemplates =
  365. {
  366. new FuncDataTemplate<Node>((x, _) => new Button { Content = x })
  367. },
  368. Items = items,
  369. };
  370. ApplyTemplates(target);
  371. var dataContexts = target.Presenter.Panel.Children
  372. .Cast<Control>()
  373. .Select(x => x.DataContext)
  374. .ToList();
  375. Assert.Equal(
  376. new object[] { items[0], items[1], "Base", "Base" },
  377. dataContexts);
  378. }
  379. [Fact]
  380. public void Control_Item_Should_Not_Be_NameScope()
  381. {
  382. var items = new object[]
  383. {
  384. new TreeViewItem(),
  385. };
  386. var target = new TreeView
  387. {
  388. Template = CreateTreeViewTemplate(),
  389. Items = items,
  390. };
  391. target.ApplyTemplate();
  392. target.Presenter.ApplyTemplate();
  393. var item = target.Presenter.Panel.LogicalChildren[0];
  394. Assert.Null(NameScope.GetNameScope((TreeViewItem)item));
  395. }
  396. [Fact]
  397. public void Should_React_To_Children_Changing()
  398. {
  399. var data = CreateTestTreeData();
  400. var target = new TreeView
  401. {
  402. Template = CreateTreeViewTemplate(),
  403. Items = data,
  404. };
  405. CreateNodeDataTemplate(target);
  406. ApplyTemplates(target);
  407. Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
  408. Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1));
  409. Assert.Equal(new[] { "Grandchild2a" }, ExtractItemHeader(target, 2));
  410. // Make sure that the binding to Node.Children does not get collected.
  411. GC.Collect();
  412. data[0].Children = new AvaloniaList<Node>
  413. {
  414. new Node
  415. {
  416. Value = "NewChild1",
  417. }
  418. };
  419. Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
  420. Assert.Equal(new[] { "NewChild1" }, ExtractItemHeader(target, 1));
  421. }
  422. [Fact]
  423. public void Keyboard_Navigation_Should_Move_To_Last_Selected_Node()
  424. {
  425. using (UnitTestApplication.Start(TestServices.RealFocus))
  426. {
  427. var focus = FocusManager.Instance;
  428. var navigation = AvaloniaLocator.Current.GetService<IKeyboardNavigationHandler>();
  429. var data = CreateTestTreeData();
  430. var target = new TreeView
  431. {
  432. Template = CreateTreeViewTemplate(),
  433. Items = data,
  434. };
  435. var button = new Button();
  436. var root = new TestRoot
  437. {
  438. Child = new StackPanel
  439. {
  440. Children = { target, button },
  441. }
  442. };
  443. CreateNodeDataTemplate(target);
  444. ApplyTemplates(target);
  445. var item = data[0].Children[0];
  446. var node = target.ItemContainerGenerator.Index.ContainerFromItem(item);
  447. Assert.NotNull(node);
  448. target.SelectedItem = item;
  449. node.Focus();
  450. Assert.Same(node, focus.Current);
  451. navigation.Move(focus.Current, NavigationDirection.Next);
  452. Assert.Same(button, focus.Current);
  453. navigation.Move(focus.Current, NavigationDirection.Next);
  454. Assert.Same(node, focus.Current);
  455. }
  456. }
  457. [Fact]
  458. public void Pressing_SelectAll_Gesture_Should_Select_All_Nodes()
  459. {
  460. using (UnitTestApplication.Start())
  461. {
  462. var tree = CreateTestTreeData();
  463. var target = new TreeView
  464. {
  465. Template = CreateTreeViewTemplate(),
  466. Items = tree,
  467. SelectionMode = SelectionMode.Multiple
  468. };
  469. var visualRoot = new TestRoot();
  470. visualRoot.Child = target;
  471. CreateNodeDataTemplate(target);
  472. ApplyTemplates(target);
  473. var rootNode = tree[0];
  474. var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
  475. var selectAllGesture = keymap.SelectAll.First();
  476. var keyEvent = new KeyEventArgs
  477. {
  478. RoutedEvent = InputElement.KeyDownEvent,
  479. Key = selectAllGesture.Key,
  480. Modifiers = selectAllGesture.Modifiers
  481. };
  482. target.RaiseEvent(keyEvent);
  483. AssertChildrenSelected(target, rootNode);
  484. }
  485. }
  486. [Fact]
  487. public void Pressing_SelectAll_Gesture_With_Downward_Range_Selected_Should_Select_All_Nodes()
  488. {
  489. using (UnitTestApplication.Start())
  490. {
  491. var tree = CreateTestTreeData();
  492. var target = new TreeView
  493. {
  494. Template = CreateTreeViewTemplate(),
  495. Items = tree,
  496. SelectionMode = SelectionMode.Multiple
  497. };
  498. var visualRoot = new TestRoot();
  499. visualRoot.Child = target;
  500. CreateNodeDataTemplate(target);
  501. ApplyTemplates(target);
  502. var rootNode = tree[0];
  503. var from = rootNode.Children[0];
  504. var to = rootNode.Children.Last();
  505. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  506. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  507. ClickContainer(fromContainer, InputModifiers.None);
  508. ClickContainer(toContainer, InputModifiers.Shift);
  509. var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
  510. var selectAllGesture = keymap.SelectAll.First();
  511. var keyEvent = new KeyEventArgs
  512. {
  513. RoutedEvent = InputElement.KeyDownEvent,
  514. Key = selectAllGesture.Key,
  515. Modifiers = selectAllGesture.Modifiers
  516. };
  517. target.RaiseEvent(keyEvent);
  518. AssertChildrenSelected(target, rootNode);
  519. }
  520. }
  521. [Fact]
  522. public void Pressing_SelectAll_Gesture_With_Upward_Range_Selected_Should_Select_All_Nodes()
  523. {
  524. using (UnitTestApplication.Start())
  525. {
  526. var tree = CreateTestTreeData();
  527. var target = new TreeView
  528. {
  529. Template = CreateTreeViewTemplate(),
  530. Items = tree,
  531. SelectionMode = SelectionMode.Multiple
  532. };
  533. var visualRoot = new TestRoot();
  534. visualRoot.Child = target;
  535. CreateNodeDataTemplate(target);
  536. ApplyTemplates(target);
  537. var rootNode = tree[0];
  538. var from = rootNode.Children.Last();
  539. var to = rootNode.Children[0];
  540. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  541. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  542. ClickContainer(fromContainer, InputModifiers.None);
  543. ClickContainer(toContainer, InputModifiers.Shift);
  544. var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
  545. var selectAllGesture = keymap.SelectAll.First();
  546. var keyEvent = new KeyEventArgs
  547. {
  548. RoutedEvent = InputElement.KeyDownEvent,
  549. Key = selectAllGesture.Key,
  550. Modifiers = selectAllGesture.Modifiers
  551. };
  552. target.RaiseEvent(keyEvent);
  553. AssertChildrenSelected(target, rootNode);
  554. }
  555. }
  556. [Fact]
  557. public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection()
  558. {
  559. var tree = CreateTestTreeData();
  560. var target = new TreeView
  561. {
  562. Template = CreateTreeViewTemplate(),
  563. Items = tree,
  564. SelectionMode = SelectionMode.Multiple,
  565. };
  566. var visualRoot = new TestRoot();
  567. visualRoot.Child = target;
  568. CreateNodeDataTemplate(target);
  569. ApplyTemplates(target);
  570. target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
  571. target.SelectAll();
  572. AssertChildrenSelected(target, tree[0]);
  573. Assert.Equal(5, target.SelectedItems.Count);
  574. _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right);
  575. Assert.Equal(5, target.SelectedItems.Count);
  576. }
  577. [Fact]
  578. public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection()
  579. {
  580. var tree = CreateTestTreeData();
  581. var target = new TreeView
  582. {
  583. Template = CreateTreeViewTemplate(),
  584. Items = tree,
  585. SelectionMode = SelectionMode.Multiple,
  586. };
  587. var visualRoot = new TestRoot();
  588. visualRoot.Child = target;
  589. CreateNodeDataTemplate(target);
  590. ApplyTemplates(target);
  591. target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
  592. var rootNode = tree[0];
  593. var to = rootNode.Children[0];
  594. var then = rootNode.Children[1];
  595. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(rootNode);
  596. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  597. var thenContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(then);
  598. ClickContainer(fromContainer, InputModifiers.None);
  599. ClickContainer(toContainer, InputModifiers.Shift);
  600. Assert.Equal(2, target.SelectedItems.Count);
  601. _mouse.Click(thenContainer, MouseButton.Right);
  602. Assert.Equal(1, target.SelectedItems.Count);
  603. }
  604. [Fact]
  605. public void Shift_Right_Click_Should_Not_Select_Multiple()
  606. {
  607. var tree = CreateTestTreeData();
  608. var target = new TreeView
  609. {
  610. Template = CreateTreeViewTemplate(),
  611. Items = tree,
  612. SelectionMode = SelectionMode.Multiple,
  613. };
  614. var visualRoot = new TestRoot();
  615. visualRoot.Child = target;
  616. CreateNodeDataTemplate(target);
  617. ApplyTemplates(target);
  618. target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
  619. var rootNode = tree[0];
  620. var from = rootNode.Children[0];
  621. var to = rootNode.Children[1];
  622. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  623. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  624. _mouse.Click(fromContainer);
  625. _mouse.Click(toContainer, MouseButton.Right, modifiers: InputModifiers.Shift);
  626. Assert.Equal(1, target.SelectedItems.Count);
  627. }
  628. [Fact]
  629. public void Ctrl_Right_Click_Should_Not_Select_Multiple()
  630. {
  631. var tree = CreateTestTreeData();
  632. var target = new TreeView
  633. {
  634. Template = CreateTreeViewTemplate(),
  635. Items = tree,
  636. SelectionMode = SelectionMode.Multiple,
  637. };
  638. var visualRoot = new TestRoot();
  639. visualRoot.Child = target;
  640. CreateNodeDataTemplate(target);
  641. ApplyTemplates(target);
  642. target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
  643. var rootNode = tree[0];
  644. var from = rootNode.Children[0];
  645. var to = rootNode.Children[1];
  646. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  647. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  648. _mouse.Click(fromContainer);
  649. _mouse.Click(toContainer, MouseButton.Right, modifiers: InputModifiers.Control);
  650. Assert.Equal(1, target.SelectedItems.Count);
  651. }
  652. private void ApplyTemplates(TreeView tree)
  653. {
  654. tree.ApplyTemplate();
  655. tree.Presenter.ApplyTemplate();
  656. ApplyTemplates(tree.Presenter.Panel.Children);
  657. }
  658. private void ApplyTemplates(IEnumerable<IControl> controls)
  659. {
  660. foreach (TreeViewItem control in controls)
  661. {
  662. control.Template = CreateTreeViewItemTemplate();
  663. control.ApplyTemplate();
  664. control.Presenter.ApplyTemplate();
  665. control.HeaderPresenter.ApplyTemplate();
  666. ApplyTemplates(control.Presenter.Panel.Children);
  667. }
  668. }
  669. private IList<Node> CreateTestTreeData()
  670. {
  671. return new AvaloniaList<Node>
  672. {
  673. new Node
  674. {
  675. Value = "Root",
  676. Children = new AvaloniaList<Node>
  677. {
  678. new Node
  679. {
  680. Value = "Child1",
  681. },
  682. new Node
  683. {
  684. Value = "Child2",
  685. Children = new AvaloniaList<Node>
  686. {
  687. new Node
  688. {
  689. Value = "Grandchild2a",
  690. },
  691. },
  692. },
  693. new Node
  694. {
  695. Value = "Child3",
  696. }
  697. }
  698. }
  699. };
  700. }
  701. private void CreateNodeDataTemplate(IControl control)
  702. {
  703. control.DataTemplates.Add(new TestTreeDataTemplate());
  704. }
  705. private IControlTemplate CreateTreeViewTemplate()
  706. {
  707. return new FuncControlTemplate<TreeView>((parent, scope) => new ItemsPresenter
  708. {
  709. Name = "PART_ItemsPresenter",
  710. [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
  711. }.RegisterInNameScope(scope));
  712. }
  713. private IControlTemplate CreateTreeViewItemTemplate()
  714. {
  715. return new FuncControlTemplate<TreeViewItem>((parent, scope) => new Panel
  716. {
  717. Children =
  718. {
  719. new ContentPresenter
  720. {
  721. Name = "PART_HeaderPresenter",
  722. [~ContentPresenter.ContentProperty] = parent[~TreeViewItem.HeaderProperty],
  723. }.RegisterInNameScope(scope),
  724. new ItemsPresenter
  725. {
  726. Name = "PART_ItemsPresenter",
  727. [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
  728. }.RegisterInNameScope(scope)
  729. }
  730. });
  731. }
  732. private List<string> ExtractItemHeader(TreeView tree, int level)
  733. {
  734. return ExtractItemContent(tree.Presenter.Panel, 0, level)
  735. .Select(x => x.Header)
  736. .OfType<TextBlock>()
  737. .Select(x => x.Text)
  738. .ToList();
  739. }
  740. private IEnumerable<TreeViewItem> ExtractItemContent(IPanel panel, int currentLevel, int level)
  741. {
  742. foreach (TreeViewItem container in panel.Children)
  743. {
  744. if (container.Template == null)
  745. {
  746. container.Template = CreateTreeViewItemTemplate();
  747. container.ApplyTemplate();
  748. }
  749. if (currentLevel == level)
  750. {
  751. yield return container;
  752. }
  753. else
  754. {
  755. foreach (var child in ExtractItemContent(container.Presenter.Panel, currentLevel + 1, level))
  756. {
  757. yield return child;
  758. }
  759. }
  760. }
  761. }
  762. void ClickContainer(IControl container, InputModifiers modifiers)
  763. {
  764. _mouse.Click(container, modifiers: modifiers);
  765. }
  766. void AssertChildrenSelected(TreeView treeView, Node rootNode)
  767. {
  768. foreach (var child in rootNode.Children)
  769. {
  770. var container = (TreeViewItem)treeView.ItemContainerGenerator.Index.ContainerFromItem(child);
  771. Assert.True(container.IsSelected);
  772. }
  773. }
  774. private class Node : NotifyingBase
  775. {
  776. private IAvaloniaList<Node> _children;
  777. public string Value { get; set; }
  778. public IAvaloniaList<Node> Children
  779. {
  780. get
  781. {
  782. return _children;
  783. }
  784. set
  785. {
  786. _children = value;
  787. RaisePropertyChanged(nameof(Children));
  788. }
  789. }
  790. }
  791. private class TestTreeDataTemplate : ITreeDataTemplate
  792. {
  793. public IControl Build(object param)
  794. {
  795. var node = (Node)param;
  796. return new TextBlock { Text = node.Value };
  797. }
  798. public bool SupportsRecycling => false;
  799. public InstancedBinding ItemsSelector(object item)
  800. {
  801. var obs = ExpressionObserver.Create(item, o => (o as Node).Children);
  802. return InstancedBinding.OneWay(obs);
  803. }
  804. public bool Match(object data)
  805. {
  806. return data is Node;
  807. }
  808. }
  809. }
  810. }