TreeViewTests.cs 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.ComponentModel;
  6. using System.Linq;
  7. using System.Runtime.CompilerServices;
  8. using Avalonia.Collections;
  9. using Avalonia.Controls.Generators;
  10. using Avalonia.Controls.Presenters;
  11. using Avalonia.Controls.Templates;
  12. using Avalonia.Data;
  13. using Avalonia.Data.Core;
  14. using Avalonia.Input;
  15. using Avalonia.Input.Platform;
  16. using Avalonia.Interactivity;
  17. using Avalonia.LogicalTree;
  18. using Avalonia.Styling;
  19. using Avalonia.UnitTests;
  20. using JetBrains.Annotations;
  21. using Moq;
  22. using Xunit;
  23. namespace Avalonia.Controls.UnitTests
  24. {
  25. public class TreeViewTests
  26. {
  27. MouseTestHelper _mouse = new MouseTestHelper();
  28. [Fact]
  29. public void Items_Should_Be_Created()
  30. {
  31. var target = new TreeView
  32. {
  33. Template = CreateTreeViewTemplate(),
  34. Items = CreateTestTreeData(),
  35. };
  36. var root = new TestRoot(target);
  37. CreateNodeDataTemplate(target);
  38. ApplyTemplates(target);
  39. Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
  40. Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1));
  41. Assert.Equal(new[] { "Grandchild2a" }, ExtractItemHeader(target, 2));
  42. }
  43. [Fact]
  44. public void Items_Should_Be_Created_Using_ItemTemplate_If_Present()
  45. {
  46. TreeView target;
  47. var root = new TestRoot
  48. {
  49. Child = target = new TreeView
  50. {
  51. Template = CreateTreeViewTemplate(),
  52. Items = CreateTestTreeData(),
  53. ItemTemplate = new FuncTreeDataTemplate<Node>(
  54. (_, __) => new Canvas(),
  55. x => x.Children),
  56. }
  57. };
  58. ApplyTemplates(target);
  59. var items = target.ItemContainerGenerator.Index.Containers
  60. .OfType<TreeViewItem>()
  61. .ToList();
  62. Assert.Equal(5, items.Count);
  63. Assert.All(items, x => Assert.IsType<Canvas>(x.HeaderPresenter.Child));
  64. }
  65. [Fact]
  66. public void Items_Should_Be_Created_Using_ItemConatinerTheme_If_Present()
  67. {
  68. TreeView target;
  69. var theme = new ControlTheme(typeof(TreeViewItem));
  70. var root = new TestRoot
  71. {
  72. Child = target = new TreeView
  73. {
  74. Template = CreateTreeViewTemplate(),
  75. Items = CreateTestTreeData(),
  76. ItemContainerTheme = theme,
  77. ItemTemplate = new FuncTreeDataTemplate<Node>(
  78. (_, __) => new Canvas(),
  79. x => x.Children),
  80. }
  81. };
  82. ApplyTemplates(target);
  83. var items = target.ItemContainerGenerator.Index.Containers
  84. .OfType<TreeViewItem>()
  85. .ToList();
  86. Assert.Equal(5, items.Count);
  87. Assert.All(items, x => Assert.Same(theme, x.ItemContainerTheme));
  88. }
  89. [Fact]
  90. public void Root_ItemContainerGenerator_Containers_Should_Be_Root_Containers()
  91. {
  92. var target = new TreeView
  93. {
  94. Template = CreateTreeViewTemplate(),
  95. Items = CreateTestTreeData(),
  96. };
  97. var root = new TestRoot(target);
  98. CreateNodeDataTemplate(target);
  99. ApplyTemplates(target);
  100. var container = (TreeViewItem)target.ItemContainerGenerator.Containers.Single().ContainerControl;
  101. var header = (TextBlock)container.Header;
  102. Assert.Equal("Root", header.Text);
  103. }
  104. [Fact]
  105. public void Root_TreeContainerFromItem_Should_Return_Descendant_Item()
  106. {
  107. var tree = CreateTestTreeData();
  108. var target = new TreeView
  109. {
  110. Template = CreateTreeViewTemplate(),
  111. Items = tree,
  112. };
  113. // For TreeViewItem to find its parent TreeView, OnAttachedToLogicalTree needs
  114. // to be called, which requires an IStyleRoot.
  115. var root = new TestRoot();
  116. root.Child = target;
  117. CreateNodeDataTemplate(target);
  118. ApplyTemplates(target);
  119. var container = target.ItemContainerGenerator.Index.ContainerFromItem(
  120. tree[0].Children[1].Children[0]);
  121. Assert.NotNull(container);
  122. var header = ((TreeViewItem)container).Header;
  123. var headerContent = ((TextBlock)header).Text;
  124. Assert.Equal("Grandchild2a", headerContent);
  125. }
  126. [Fact]
  127. public void Clicking_Item_Should_Select_It()
  128. {
  129. using (Application())
  130. {
  131. var tree = CreateTestTreeData();
  132. var target = new TreeView
  133. {
  134. Template = CreateTreeViewTemplate(),
  135. Items = tree,
  136. };
  137. var visualRoot = new TestRoot();
  138. visualRoot.Child = target;
  139. CreateNodeDataTemplate(target);
  140. ApplyTemplates(target);
  141. ExpandAll(target);
  142. var item = tree[0].Children[1].Children[0];
  143. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  144. Assert.NotNull(container);
  145. _mouse.Click(container);
  146. Assert.Equal(item, target.SelectedItem);
  147. Assert.True(container.IsSelected);
  148. }
  149. }
  150. [Fact]
  151. public void Clicking_WithControlModifier_Selected_Item_Should_Deselect_It()
  152. {
  153. using (Application())
  154. {
  155. var tree = CreateTestTreeData();
  156. var target = new TreeView
  157. {
  158. Template = CreateTreeViewTemplate(),
  159. Items = tree
  160. };
  161. var visualRoot = new TestRoot();
  162. visualRoot.Child = target;
  163. CreateNodeDataTemplate(target);
  164. ApplyTemplates(target);
  165. ExpandAll(target);
  166. var item = tree[0].Children[1].Children[0];
  167. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  168. Assert.NotNull(container);
  169. target.SelectedItem = item;
  170. Assert.True(container.IsSelected);
  171. _mouse.Click(container, modifiers: KeyModifiers.Control);
  172. Assert.Null(target.SelectedItem);
  173. Assert.False(container.IsSelected);
  174. }
  175. }
  176. [Fact]
  177. public void Clicking_WithControlModifier_Not_Selected_Item_Should_Select_It()
  178. {
  179. using (Application())
  180. {
  181. var tree = CreateTestTreeData();
  182. var target = new TreeView
  183. {
  184. Template = CreateTreeViewTemplate(),
  185. Items = tree
  186. };
  187. var visualRoot = new TestRoot();
  188. visualRoot.Child = target;
  189. CreateNodeDataTemplate(target);
  190. ApplyTemplates(target);
  191. ExpandAll(target);
  192. var item1 = tree[0].Children[1].Children[0];
  193. var container1 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
  194. var item2 = tree[0].Children[1];
  195. var container2 = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2);
  196. Assert.NotNull(container1);
  197. Assert.NotNull(container2);
  198. target.SelectedItem = item1;
  199. Assert.True(container1.IsSelected);
  200. _mouse.Click(container2, modifiers: KeyModifiers.Control);
  201. Assert.Equal(item2, target.SelectedItem);
  202. Assert.False(container1.IsSelected);
  203. Assert.True(container2.IsSelected);
  204. }
  205. }
  206. [Fact]
  207. public void Clicking_WithControlModifier_Selected_Item_Should_Deselect_And_Remove_From_SelectedItems()
  208. {
  209. using (Application())
  210. {
  211. var tree = CreateTestTreeData();
  212. var target = new TreeView
  213. {
  214. Template = CreateTreeViewTemplate(),
  215. Items = tree,
  216. SelectionMode = SelectionMode.Multiple
  217. };
  218. var visualRoot = new TestRoot();
  219. visualRoot.Child = target;
  220. CreateNodeDataTemplate(target);
  221. ApplyTemplates(target);
  222. ExpandAll(target);
  223. var rootNode = tree[0];
  224. var item1 = rootNode.Children[0];
  225. var item2 = rootNode.Children.Last();
  226. var item1Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item1);
  227. var item2Container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item2);
  228. ClickContainer(item1Container, KeyModifiers.Control);
  229. Assert.True(item1Container.IsSelected);
  230. ClickContainer(item2Container, KeyModifiers.Control);
  231. Assert.True(item2Container.IsSelected);
  232. Assert.Equal(new[] { item1, item2 }, target.SelectedItems.OfType<Node>());
  233. ClickContainer(item1Container, KeyModifiers.Control);
  234. Assert.False(item1Container.IsSelected);
  235. Assert.DoesNotContain(item1, target.SelectedItems.OfType<Node>());
  236. }
  237. }
  238. [Fact]
  239. public void Clicking_WithShiftModifier_DownDirection_Should_Select_Range_Of_Items()
  240. {
  241. using (Application())
  242. {
  243. var tree = CreateTestTreeData();
  244. var target = new TreeView
  245. {
  246. Template = CreateTreeViewTemplate(),
  247. Items = tree,
  248. SelectionMode = SelectionMode.Multiple
  249. };
  250. var visualRoot = new TestRoot();
  251. visualRoot.Child = target;
  252. CreateNodeDataTemplate(target);
  253. ApplyTemplates(target);
  254. ExpandAll(target);
  255. var rootNode = tree[0];
  256. var from = rootNode.Children[0];
  257. var to = rootNode.Children.Last();
  258. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  259. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  260. ClickContainer(fromContainer, KeyModifiers.None);
  261. Assert.True(fromContainer.IsSelected);
  262. ClickContainer(toContainer, KeyModifiers.Shift);
  263. AssertChildrenSelected(target, rootNode);
  264. }
  265. }
  266. [Fact]
  267. public void Clicking_WithShiftModifier_UpDirection_Should_Select_Range_Of_Items()
  268. {
  269. using (Application())
  270. {
  271. var tree = CreateTestTreeData();
  272. var target = new TreeView
  273. {
  274. Template = CreateTreeViewTemplate(),
  275. Items = tree,
  276. SelectionMode = SelectionMode.Multiple
  277. };
  278. var visualRoot = new TestRoot();
  279. visualRoot.Child = target;
  280. CreateNodeDataTemplate(target);
  281. ApplyTemplates(target);
  282. ExpandAll(target);
  283. var rootNode = tree[0];
  284. var from = rootNode.Children.Last();
  285. var to = rootNode.Children[0];
  286. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  287. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  288. ClickContainer(fromContainer, KeyModifiers.None);
  289. Assert.True(fromContainer.IsSelected);
  290. ClickContainer(toContainer, KeyModifiers.Shift);
  291. AssertChildrenSelected(target, rootNode);
  292. }
  293. }
  294. [Fact]
  295. public void Clicking_First_Item_Of_SelectedItems_Should_Select_Only_It()
  296. {
  297. using (Application())
  298. {
  299. var tree = CreateTestTreeData();
  300. var target = new TreeView
  301. {
  302. Template = CreateTreeViewTemplate(),
  303. Items = tree,
  304. SelectionMode = SelectionMode.Multiple
  305. };
  306. var visualRoot = new TestRoot();
  307. visualRoot.Child = target;
  308. CreateNodeDataTemplate(target);
  309. ApplyTemplates(target);
  310. ExpandAll(target);
  311. var rootNode = tree[0];
  312. var from = rootNode.Children.Last();
  313. var to = rootNode.Children[0];
  314. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  315. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  316. ClickContainer(fromContainer, KeyModifiers.None);
  317. ClickContainer(toContainer, KeyModifiers.Shift);
  318. AssertChildrenSelected(target, rootNode);
  319. ClickContainer(fromContainer, KeyModifiers.None);
  320. Assert.True(fromContainer.IsSelected);
  321. foreach (var child in rootNode.Children)
  322. {
  323. if (child == from)
  324. {
  325. continue;
  326. }
  327. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(child);
  328. Assert.False(container.IsSelected);
  329. }
  330. }
  331. }
  332. [Fact]
  333. public void Double_Clicking_Item_Header_Should_Expand_It()
  334. {
  335. using (Application())
  336. {
  337. var tree = CreateTestTreeData();
  338. var target = new TreeView
  339. {
  340. Template = CreateTreeViewTemplate(),
  341. Items = tree,
  342. };
  343. var visualRoot = new TestRoot();
  344. visualRoot.Child = target;
  345. CreateNodeDataTemplate(target);
  346. ApplyTemplates(target);
  347. CollapseAll(target);
  348. var item = tree[0].Children[1];
  349. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  350. Assert.NotNull(container);
  351. Assert.False(container.IsExpanded);
  352. var header = container.Header as Interactive;
  353. Assert.NotNull(header);
  354. _mouse.DoubleClick(header);
  355. Assert.True(container.IsExpanded);
  356. }
  357. }
  358. [Fact]
  359. public void Double_Clicking_Item_Header_With_No_Children_Does_Not_Expand_It()
  360. {
  361. using (Application())
  362. {
  363. var tree = CreateTestTreeData();
  364. var target = new TreeView
  365. {
  366. Template = CreateTreeViewTemplate(),
  367. Items = tree,
  368. };
  369. var visualRoot = new TestRoot();
  370. visualRoot.Child = target;
  371. CreateNodeDataTemplate(target);
  372. ApplyTemplates(target);
  373. CollapseAll(target);
  374. var item = tree[0].Children[1].Children[0];
  375. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  376. Assert.NotNull(container);
  377. Assert.False(container.IsExpanded);
  378. var header = container.Header as Interactive;
  379. Assert.NotNull(header);
  380. _mouse.DoubleClick(header);
  381. Assert.False(container.IsExpanded);
  382. }
  383. }
  384. [Fact]
  385. public void Double_Clicking_Item_Header_Should_Collapse_It()
  386. {
  387. using (Application())
  388. {
  389. var tree = CreateTestTreeData();
  390. var target = new TreeView
  391. {
  392. Template = CreateTreeViewTemplate(),
  393. Items = tree,
  394. };
  395. var visualRoot = new TestRoot();
  396. visualRoot.Child = target;
  397. CreateNodeDataTemplate(target);
  398. ApplyTemplates(target);
  399. ExpandAll(target);
  400. var item = tree[0].Children[1];
  401. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  402. Assert.NotNull(container);
  403. Assert.True(container.IsExpanded);
  404. var header = container.Header as Interactive;
  405. Assert.NotNull(header);
  406. _mouse.DoubleClick(header);
  407. Assert.False(container.IsExpanded);
  408. }
  409. }
  410. [Fact]
  411. public void Enter_Key_Should_Collapse_TreeViewItem()
  412. {
  413. using (Application())
  414. {
  415. var tree = CreateTestTreeData();
  416. var target = new TreeView
  417. {
  418. Template = CreateTreeViewTemplate(),
  419. Items = tree,
  420. };
  421. var visualRoot = new TestRoot();
  422. visualRoot.Child = target;
  423. CreateNodeDataTemplate(target);
  424. ApplyTemplates(target);
  425. ExpandAll(target); // NOTE this line
  426. var item = tree[0];
  427. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  428. Assert.NotNull(container);
  429. Assert.True(container.IsExpanded);
  430. var header = container.Header as Interactive;
  431. Assert.NotNull(header);
  432. container.RaiseEvent(new KeyEventArgs
  433. {
  434. RoutedEvent = InputElement.KeyDownEvent,
  435. Key = Key.Enter,
  436. });
  437. Assert.False(container.IsExpanded);
  438. }
  439. }
  440. [Fact]
  441. public void Enter_plus_Ctrl_Key_Should_Collapse_TreeViewItem_Recursively()
  442. {
  443. using (Application())
  444. {
  445. var tree = CreateTestTreeData();
  446. var target = new TreeView
  447. {
  448. Template = CreateTreeViewTemplate(),
  449. Items = tree,
  450. };
  451. var visualRoot = new TestRoot();
  452. visualRoot.Child = target;
  453. CreateNodeDataTemplate(target);
  454. ApplyTemplates(target);
  455. ExpandAll(target); // NOTE this line
  456. var item = tree[0];
  457. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  458. Assert.NotNull(container);
  459. Assert.True(container.IsExpanded);
  460. var header = container.Header as Interactive;
  461. Assert.NotNull(header);
  462. container.RaiseEvent(new KeyEventArgs
  463. {
  464. RoutedEvent = InputElement.KeyDownEvent,
  465. Key = Key.Enter,
  466. KeyModifiers = KeyModifiers.Control,
  467. });
  468. Assert.False(container.IsExpanded);
  469. AssertEachItemWithChildrenIsCollapsed(item);
  470. void AssertEachItemWithChildrenIsCollapsed(Node node)
  471. {
  472. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(node);
  473. Assert.NotNull(container);
  474. if (node.Children?.Count > 0)
  475. {
  476. Assert.False(container.IsExpanded);
  477. foreach (var c in node.Children)
  478. {
  479. AssertEachItemWithChildrenIsCollapsed(c);
  480. }
  481. }
  482. else
  483. {
  484. Assert.True(container.IsExpanded);
  485. }
  486. }
  487. }
  488. }
  489. [Fact]
  490. public void Enter_Key_Should_Expand_TreeViewItem()
  491. {
  492. using (Application())
  493. {
  494. var tree = CreateTestTreeData();
  495. var target = new TreeView
  496. {
  497. Template = CreateTreeViewTemplate(),
  498. Items = tree,
  499. };
  500. var visualRoot = new TestRoot();
  501. visualRoot.Child = target;
  502. CreateNodeDataTemplate(target);
  503. ApplyTemplates(target);
  504. CollapseAll(target); // NOTE this line
  505. var item = tree[0];
  506. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  507. Assert.NotNull(container);
  508. Assert.False(container.IsExpanded);
  509. var header = container.Header as Interactive;
  510. Assert.NotNull(header);
  511. container.RaiseEvent(new KeyEventArgs
  512. {
  513. RoutedEvent = InputElement.KeyDownEvent,
  514. Key = Key.Enter,
  515. });
  516. Assert.True(container.IsExpanded);
  517. }
  518. }
  519. [Fact]
  520. public void Enter_plus_Ctrl_Key_Should_Expand_TreeViewItem_Recursively()
  521. {
  522. using (Application())
  523. {
  524. var tree = CreateTestTreeData();
  525. var target = new TreeView
  526. {
  527. Template = CreateTreeViewTemplate(),
  528. Items = tree,
  529. };
  530. var visualRoot = new TestRoot();
  531. visualRoot.Child = target;
  532. CreateNodeDataTemplate(target);
  533. ApplyTemplates(target);
  534. CollapseAll(target); // NOTE this line
  535. var item = tree[0];
  536. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  537. Assert.NotNull(container);
  538. Assert.False(container.IsExpanded);
  539. var header = container.Header as Interactive;
  540. Assert.NotNull(header);
  541. container.RaiseEvent(new KeyEventArgs
  542. {
  543. RoutedEvent = InputElement.KeyDownEvent,
  544. Key = Key.Enter,
  545. KeyModifiers = KeyModifiers.Control,
  546. });
  547. Assert.True(container.IsExpanded);
  548. AssertEachItemWithChildrenIsExpanded(item);
  549. void AssertEachItemWithChildrenIsExpanded(Node node)
  550. {
  551. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(node);
  552. Assert.NotNull(container);
  553. if (node.Children?.Count > 0)
  554. {
  555. Assert.True(container.IsExpanded);
  556. foreach (var c in node.Children)
  557. {
  558. AssertEachItemWithChildrenIsExpanded(c);
  559. }
  560. }
  561. else
  562. {
  563. Assert.False(container.IsExpanded);
  564. }
  565. }
  566. }
  567. }
  568. [Fact]
  569. public void Space_Key_Should_Collapse_TreeViewItem()
  570. {
  571. using (Application())
  572. {
  573. var tree = CreateTestTreeData();
  574. var target = new TreeView
  575. {
  576. Template = CreateTreeViewTemplate(),
  577. Items = tree,
  578. };
  579. var visualRoot = new TestRoot();
  580. visualRoot.Child = target;
  581. CreateNodeDataTemplate(target);
  582. ApplyTemplates(target);
  583. ExpandAll(target); // NOTE this line
  584. var item = tree[0];
  585. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  586. Assert.NotNull(container);
  587. Assert.True(container.IsExpanded);
  588. var header = container.Header as Interactive;
  589. Assert.NotNull(header);
  590. container.RaiseEvent(new KeyEventArgs
  591. {
  592. RoutedEvent = InputElement.KeyDownEvent,
  593. Key = Key.Enter,
  594. });
  595. Assert.False(container.IsExpanded);
  596. }
  597. }
  598. [Fact]
  599. public void Space_plus_Ctrl_Key_Should_Collapse_TreeViewItem_Recursively()
  600. {
  601. using (Application())
  602. {
  603. var tree = CreateTestTreeData();
  604. var target = new TreeView
  605. {
  606. Template = CreateTreeViewTemplate(),
  607. Items = tree,
  608. };
  609. var visualRoot = new TestRoot();
  610. visualRoot.Child = target;
  611. CreateNodeDataTemplate(target);
  612. ApplyTemplates(target);
  613. ExpandAll(target); // NOTE this line
  614. var item = tree[0];
  615. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  616. Assert.NotNull(container);
  617. Assert.True(container.IsExpanded);
  618. var header = container.Header as Interactive;
  619. Assert.NotNull(header);
  620. container.RaiseEvent(new KeyEventArgs
  621. {
  622. RoutedEvent = InputElement.KeyDownEvent,
  623. Key = Key.Enter,
  624. KeyModifiers = KeyModifiers.Control,
  625. });
  626. Assert.False(container.IsExpanded);
  627. AssertEachItemWithChildrenIsCollapsed(item);
  628. void AssertEachItemWithChildrenIsCollapsed(Node node)
  629. {
  630. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(node);
  631. Assert.NotNull(container);
  632. if (node.Children?.Count > 0)
  633. {
  634. Assert.False(container.IsExpanded);
  635. foreach (var c in node.Children)
  636. {
  637. AssertEachItemWithChildrenIsCollapsed(c);
  638. }
  639. }
  640. else
  641. {
  642. Assert.True(container.IsExpanded);
  643. }
  644. }
  645. }
  646. }
  647. [Fact]
  648. public void Space_Key_Should_Expand_TreeViewItem()
  649. {
  650. using (Application())
  651. {
  652. var tree = CreateTestTreeData();
  653. var target = new TreeView
  654. {
  655. Template = CreateTreeViewTemplate(),
  656. Items = tree,
  657. };
  658. var visualRoot = new TestRoot();
  659. visualRoot.Child = target;
  660. CreateNodeDataTemplate(target);
  661. ApplyTemplates(target);
  662. CollapseAll(target); // NOTE this line
  663. var item = tree[0];
  664. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  665. Assert.NotNull(container);
  666. Assert.False(container.IsExpanded);
  667. var header = container.Header as Interactive;
  668. Assert.NotNull(header);
  669. container.RaiseEvent(new KeyEventArgs
  670. {
  671. RoutedEvent = InputElement.KeyDownEvent,
  672. Key = Key.Enter,
  673. });
  674. Assert.True(container.IsExpanded);
  675. }
  676. }
  677. [Fact]
  678. public void Space_plus_Ctrl_Key_Should_Expand_TreeViewItem_Recursively()
  679. {
  680. using (Application())
  681. {
  682. var tree = CreateTestTreeData();
  683. var target = new TreeView
  684. {
  685. Template = CreateTreeViewTemplate(),
  686. Items = tree,
  687. };
  688. var visualRoot = new TestRoot();
  689. visualRoot.Child = target;
  690. CreateNodeDataTemplate(target);
  691. ApplyTemplates(target);
  692. CollapseAll(target); // NOTE this line
  693. var item = tree[0];
  694. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  695. Assert.NotNull(container);
  696. Assert.False(container.IsExpanded);
  697. var header = container.Header as Interactive;
  698. Assert.NotNull(header);
  699. container.RaiseEvent(new KeyEventArgs
  700. {
  701. RoutedEvent = InputElement.KeyDownEvent,
  702. Key = Key.Enter,
  703. KeyModifiers = KeyModifiers.Control,
  704. });
  705. Assert.True(container.IsExpanded);
  706. AssertEachItemWithChildrenIsExpanded(item);
  707. void AssertEachItemWithChildrenIsExpanded(Node node)
  708. {
  709. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(node);
  710. Assert.NotNull(container);
  711. if (node.Children?.Count > 0)
  712. {
  713. Assert.True(container.IsExpanded);
  714. foreach (var c in node.Children)
  715. {
  716. AssertEachItemWithChildrenIsExpanded(c);
  717. }
  718. }
  719. else
  720. {
  721. Assert.False(container.IsExpanded);
  722. }
  723. }
  724. }
  725. }
  726. [Fact]
  727. public void Numpad_Star_Should_Expand_All_Children_Recursively()
  728. {
  729. using (Application())
  730. {
  731. var tree = CreateTestTreeData();
  732. var target = new TreeView
  733. {
  734. Template = CreateTreeViewTemplate(),
  735. Items = tree,
  736. };
  737. var visualRoot = new TestRoot();
  738. visualRoot.Child = target;
  739. CreateNodeDataTemplate(target);
  740. ApplyTemplates(target);
  741. CollapseAll(target);
  742. var item = tree[0];
  743. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  744. Assert.NotNull(container);
  745. container.RaiseEvent(new KeyEventArgs
  746. {
  747. RoutedEvent = InputElement.KeyDownEvent,
  748. Key = Key.Multiply,
  749. });
  750. AssertEachItemWithChildrenIsExpanded(item);
  751. void AssertEachItemWithChildrenIsExpanded(Node node)
  752. {
  753. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(node);
  754. Assert.NotNull(container);
  755. if (node.Children?.Count > 0)
  756. {
  757. Assert.True(container.IsExpanded);
  758. foreach (var c in node.Children)
  759. {
  760. AssertEachItemWithChildrenIsExpanded(c);
  761. }
  762. }
  763. else
  764. {
  765. Assert.False(container.IsExpanded);
  766. }
  767. }
  768. }
  769. }
  770. [Fact]
  771. public void Numpad_Slash_Should_Collapse_All_Children_Recursively()
  772. {
  773. using (Application())
  774. {
  775. var tree = CreateTestTreeData();
  776. var target = new TreeView
  777. {
  778. Template = CreateTreeViewTemplate(),
  779. Items = tree,
  780. };
  781. var visualRoot = new TestRoot();
  782. visualRoot.Child = target;
  783. CreateNodeDataTemplate(target);
  784. ApplyTemplates(target);
  785. ExpandAll(target);
  786. var item = tree[0];
  787. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  788. Assert.NotNull(container);
  789. container.RaiseEvent(new KeyEventArgs
  790. {
  791. RoutedEvent = InputElement.KeyDownEvent,
  792. Key = Key.Divide,
  793. });
  794. AssertEachItemWithChildrenIsCollapsed(item);
  795. void AssertEachItemWithChildrenIsCollapsed(Node node)
  796. {
  797. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(node);
  798. Assert.NotNull(container);
  799. if (node.Children?.Count > 0)
  800. {
  801. Assert.False(container.IsExpanded);
  802. foreach (var c in node.Children)
  803. {
  804. AssertEachItemWithChildrenIsCollapsed(c);
  805. }
  806. }
  807. else
  808. {
  809. Assert.True(container.IsExpanded);
  810. }
  811. }
  812. }
  813. }
  814. [Fact]
  815. public void Setting_SelectedItem_Should_Set_Container_Selected()
  816. {
  817. using (Application())
  818. {
  819. var tree = CreateTestTreeData();
  820. var target = new TreeView
  821. {
  822. Template = CreateTreeViewTemplate(),
  823. Items = tree,
  824. };
  825. var visualRoot = new TestRoot();
  826. visualRoot.Child = target;
  827. CreateNodeDataTemplate(target);
  828. ApplyTemplates(target);
  829. ExpandAll(target);
  830. var item = tree[0].Children[1].Children[0];
  831. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  832. Assert.NotNull(container);
  833. target.SelectedItem = item;
  834. Assert.True(container.IsSelected);
  835. }
  836. }
  837. [Fact]
  838. public void Setting_SelectedItem_Should_Raise_SelectedItemChanged_Event()
  839. {
  840. using (Application())
  841. {
  842. var tree = CreateTestTreeData();
  843. var target = new TreeView
  844. {
  845. Template = CreateTreeViewTemplate(),
  846. Items = tree,
  847. };
  848. var visualRoot = new TestRoot();
  849. visualRoot.Child = target;
  850. CreateNodeDataTemplate(target);
  851. ApplyTemplates(target);
  852. ExpandAll(target);
  853. var item = tree[0].Children[1].Children[0];
  854. var called = false;
  855. target.SelectionChanged += (s, e) =>
  856. {
  857. Assert.Empty(e.RemovedItems);
  858. Assert.Equal(1, e.AddedItems.Count);
  859. Assert.Same(item, e.AddedItems[0]);
  860. called = true;
  861. };
  862. target.SelectedItem = item;
  863. Assert.True(called);
  864. }
  865. }
  866. [Fact]
  867. public void Bound_SelectedItem_Should_Not_Be_Cleared_when_Changing_Selection()
  868. {
  869. using (Application())
  870. {
  871. var dataContext = new TestDataContext();
  872. var target = new TreeView
  873. {
  874. Template = CreateTreeViewTemplate(),
  875. DataContext = dataContext
  876. };
  877. target.Bind(TreeView.ItemsProperty, new Binding("Items"));
  878. target.Bind(TreeView.SelectedItemProperty, new Binding("SelectedItem"));
  879. var visualRoot = new TestRoot();
  880. visualRoot.Child = target;
  881. CreateNodeDataTemplate(target);
  882. ApplyTemplates(target);
  883. var selectedValues = new List<object>();
  884. dataContext.PropertyChanged += (_, e) =>
  885. {
  886. if (e.PropertyName == nameof(TestDataContext.SelectedItem))
  887. selectedValues.Add(dataContext.SelectedItem);
  888. };
  889. selectedValues.Add(dataContext.SelectedItem);
  890. _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Left);
  891. _mouse.Click((Interactive)target.Presenter.Panel.Children[2], MouseButton.Left);
  892. Assert.Equal(3, selectedValues.Count);
  893. Assert.Equal(new[] { null, "Item 0", "Item 2" }, selectedValues.ToArray());
  894. }
  895. }
  896. [Fact]
  897. public void LogicalChildren_Should_Be_Set()
  898. {
  899. var target = new TreeView
  900. {
  901. Template = CreateTreeViewTemplate(),
  902. Items = new[] { "Foo", "Bar", "Baz " },
  903. };
  904. ApplyTemplates(target);
  905. var result = target.GetLogicalChildren()
  906. .OfType<TreeViewItem>()
  907. .Select(x => x.Header)
  908. .OfType<TextBlock>()
  909. .Select(x => x.Text)
  910. .ToList();
  911. Assert.Equal(new[] { "Foo", "Bar", "Baz " }, result);
  912. }
  913. [Fact]
  914. public void Removing_Item_Should_Remove_Itself_And_Children_From_Index()
  915. {
  916. var tree = CreateTestTreeData();
  917. var target = new TreeView
  918. {
  919. Template = CreateTreeViewTemplate(),
  920. Items = tree,
  921. };
  922. var root = new TestRoot();
  923. root.Child = target;
  924. CreateNodeDataTemplate(target);
  925. ApplyTemplates(target);
  926. Assert.Equal(5, target.ItemContainerGenerator.Index.Containers.Count());
  927. tree[0].Children.RemoveAt(1);
  928. Assert.Equal(3, target.ItemContainerGenerator.Index.Containers.Count());
  929. }
  930. [Fact]
  931. public void DataContexts_Should_Be_Correctly_Set()
  932. {
  933. var items = new object[]
  934. {
  935. "Foo",
  936. new Node { Value = "Bar" },
  937. new TextBlock { Text = "Baz" },
  938. new TreeViewItem { Header = "Qux" },
  939. };
  940. var target = new TreeView
  941. {
  942. Template = CreateTreeViewTemplate(),
  943. DataContext = "Base",
  944. DataTemplates =
  945. {
  946. new FuncDataTemplate<Node>((x, _) => new Button { Content = x })
  947. },
  948. Items = items,
  949. };
  950. ApplyTemplates(target);
  951. var dataContexts = target.Presenter.Panel.Children
  952. .Cast<Control>()
  953. .Select(x => x.DataContext)
  954. .ToList();
  955. Assert.Equal(
  956. new object[] { items[0], items[1], "Base", "Base" },
  957. dataContexts);
  958. }
  959. [Fact]
  960. public void Control_Item_Should_Not_Be_NameScope()
  961. {
  962. var items = new object[]
  963. {
  964. new TreeViewItem(),
  965. };
  966. var target = new TreeView
  967. {
  968. Template = CreateTreeViewTemplate(),
  969. Items = items,
  970. };
  971. target.ApplyTemplate();
  972. target.Presenter.ApplyTemplate();
  973. var item = target.Presenter.Panel.LogicalChildren[0];
  974. Assert.Null(NameScope.GetNameScope((TreeViewItem)item));
  975. }
  976. [Fact]
  977. public void Should_React_To_Children_Changing()
  978. {
  979. var data = CreateTestTreeData();
  980. var target = new TreeView
  981. {
  982. Template = CreateTreeViewTemplate(),
  983. Items = data,
  984. };
  985. var root = new TestRoot(target);
  986. CreateNodeDataTemplate(target);
  987. ApplyTemplates(target);
  988. Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
  989. Assert.Equal(new[] { "Child1", "Child2", "Child3" }, ExtractItemHeader(target, 1));
  990. Assert.Equal(new[] { "Grandchild2a" }, ExtractItemHeader(target, 2));
  991. // Make sure that the binding to Node.Children does not get collected.
  992. GC.Collect();
  993. data[0].Children = new AvaloniaList<Node>
  994. {
  995. new Node
  996. {
  997. Value = "NewChild1",
  998. }
  999. };
  1000. Assert.Equal(new[] { "Root" }, ExtractItemHeader(target, 0));
  1001. Assert.Equal(new[] { "NewChild1" }, ExtractItemHeader(target, 1));
  1002. }
  1003. [Fact]
  1004. public void Keyboard_Navigation_Should_Move_To_Last_Selected_Node()
  1005. {
  1006. using (Application())
  1007. {
  1008. var focus = FocusManager.Instance;
  1009. var navigation = AvaloniaLocator.Current.GetService<IKeyboardNavigationHandler>();
  1010. var data = CreateTestTreeData();
  1011. var target = new TreeView
  1012. {
  1013. Template = CreateTreeViewTemplate(),
  1014. Items = data,
  1015. };
  1016. var button = new Button();
  1017. var root = new TestRoot
  1018. {
  1019. Child = new StackPanel
  1020. {
  1021. Children = { target, button },
  1022. }
  1023. };
  1024. CreateNodeDataTemplate(target);
  1025. ApplyTemplates(target);
  1026. ExpandAll(target);
  1027. var item = data[0].Children[0];
  1028. var node = target.ItemContainerGenerator.Index.ContainerFromItem(item);
  1029. Assert.NotNull(node);
  1030. target.SelectedItem = item;
  1031. node.Focus();
  1032. Assert.Same(node, focus.Current);
  1033. navigation.Move(focus.Current, NavigationDirection.Next);
  1034. Assert.Same(button, focus.Current);
  1035. navigation.Move(focus.Current, NavigationDirection.Next);
  1036. Assert.Same(node, focus.Current);
  1037. }
  1038. }
  1039. [Fact]
  1040. public void Keyboard_Navigation_Should_Not_Crash_If_Selected_Item_Is_not_In_Tree()
  1041. {
  1042. using (Application())
  1043. {
  1044. var focus = FocusManager.Instance;
  1045. var navigation = AvaloniaLocator.Current.GetService<IKeyboardNavigationHandler>();
  1046. var data = CreateTestTreeData();
  1047. var selectedNode = new Node { Value = "Out of Tree Selected Item" };
  1048. var target = new TreeView
  1049. {
  1050. Template = CreateTreeViewTemplate(),
  1051. Items = data,
  1052. SelectedItem = selectedNode
  1053. };
  1054. var button = new Button();
  1055. var root = new TestRoot
  1056. {
  1057. Child = new StackPanel
  1058. {
  1059. Children = { target, button },
  1060. }
  1061. };
  1062. CreateNodeDataTemplate(target);
  1063. ApplyTemplates(target);
  1064. ExpandAll(target);
  1065. var item = data[0].Children[0];
  1066. var node = target.ItemContainerGenerator.Index.ContainerFromItem(item);
  1067. Assert.NotNull(node);
  1068. target.SelectedItem = selectedNode;
  1069. node.Focus();
  1070. Assert.Same(node, focus.Current);
  1071. var next = KeyboardNavigationHandler.GetNext(node, NavigationDirection.Previous);
  1072. }
  1073. }
  1074. [Fact]
  1075. public void Pressing_SelectAll_Gesture_Should_Select_All_Nodes()
  1076. {
  1077. using (UnitTestApplication.Start())
  1078. {
  1079. var tree = CreateTestTreeData();
  1080. var target = new TreeView
  1081. {
  1082. Template = CreateTreeViewTemplate(),
  1083. Items = tree,
  1084. SelectionMode = SelectionMode.Multiple
  1085. };
  1086. var visualRoot = new TestRoot();
  1087. visualRoot.Child = target;
  1088. CreateNodeDataTemplate(target);
  1089. ApplyTemplates(target);
  1090. ExpandAll(target);
  1091. var rootNode = tree[0];
  1092. var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
  1093. var selectAllGesture = keymap.SelectAll.First();
  1094. var keyEvent = new KeyEventArgs
  1095. {
  1096. RoutedEvent = InputElement.KeyDownEvent,
  1097. Key = selectAllGesture.Key,
  1098. KeyModifiers = selectAllGesture.KeyModifiers
  1099. };
  1100. target.RaiseEvent(keyEvent);
  1101. AssertChildrenSelected(target, rootNode);
  1102. }
  1103. }
  1104. [Fact]
  1105. public void Pressing_SelectAll_Gesture_With_Downward_Range_Selected_Should_Select_All_Nodes()
  1106. {
  1107. using (Application())
  1108. {
  1109. var tree = CreateTestTreeData();
  1110. var target = new TreeView
  1111. {
  1112. Template = CreateTreeViewTemplate(),
  1113. Items = tree,
  1114. SelectionMode = SelectionMode.Multiple
  1115. };
  1116. var visualRoot = new TestRoot();
  1117. visualRoot.Child = target;
  1118. CreateNodeDataTemplate(target);
  1119. ApplyTemplates(target);
  1120. ExpandAll(target);
  1121. var rootNode = tree[0];
  1122. var from = rootNode.Children[0];
  1123. var to = rootNode.Children.Last();
  1124. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  1125. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  1126. ClickContainer(fromContainer, KeyModifiers.None);
  1127. ClickContainer(toContainer, KeyModifiers.Shift);
  1128. var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
  1129. var selectAllGesture = keymap.SelectAll.First();
  1130. var keyEvent = new KeyEventArgs
  1131. {
  1132. RoutedEvent = InputElement.KeyDownEvent,
  1133. Key = selectAllGesture.Key,
  1134. KeyModifiers = selectAllGesture.KeyModifiers
  1135. };
  1136. target.RaiseEvent(keyEvent);
  1137. AssertChildrenSelected(target, rootNode);
  1138. }
  1139. }
  1140. [Fact]
  1141. public void Pressing_SelectAll_Gesture_With_Upward_Range_Selected_Should_Select_All_Nodes()
  1142. {
  1143. using (Application())
  1144. {
  1145. var tree = CreateTestTreeData();
  1146. var target = new TreeView
  1147. {
  1148. Template = CreateTreeViewTemplate(),
  1149. Items = tree,
  1150. SelectionMode = SelectionMode.Multiple
  1151. };
  1152. var visualRoot = new TestRoot();
  1153. visualRoot.Child = target;
  1154. CreateNodeDataTemplate(target);
  1155. ApplyTemplates(target);
  1156. ExpandAll(target);
  1157. var rootNode = tree[0];
  1158. var from = rootNode.Children.Last();
  1159. var to = rootNode.Children[0];
  1160. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  1161. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  1162. ClickContainer(fromContainer, KeyModifiers.None);
  1163. ClickContainer(toContainer, KeyModifiers.Shift);
  1164. var keymap = AvaloniaLocator.Current.GetService<PlatformHotkeyConfiguration>();
  1165. var selectAllGesture = keymap.SelectAll.First();
  1166. var keyEvent = new KeyEventArgs
  1167. {
  1168. RoutedEvent = InputElement.KeyDownEvent,
  1169. Key = selectAllGesture.Key,
  1170. KeyModifiers = selectAllGesture.KeyModifiers
  1171. };
  1172. target.RaiseEvent(keyEvent);
  1173. AssertChildrenSelected(target, rootNode);
  1174. }
  1175. }
  1176. [Fact]
  1177. public void Right_Click_On_SelectedItem_Should_Not_Clear_Existing_Selection()
  1178. {
  1179. using (UnitTestApplication.Start())
  1180. {
  1181. var tree = CreateTestTreeData();
  1182. var target = new TreeView
  1183. {
  1184. Template = CreateTreeViewTemplate(),
  1185. Items = tree,
  1186. SelectionMode = SelectionMode.Multiple,
  1187. };
  1188. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
  1189. var visualRoot = new TestRoot();
  1190. visualRoot.Child = target;
  1191. CreateNodeDataTemplate(target);
  1192. ApplyTemplates(target);
  1193. target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
  1194. target.SelectAll();
  1195. AssertChildrenSelected(target, tree[0]);
  1196. Assert.Equal(5, target.SelectedItems.Count);
  1197. _mouse.Click((Interactive)target.Presenter.Panel.Children[0], MouseButton.Right);
  1198. Assert.Equal(5, target.SelectedItems.Count);
  1199. }
  1200. }
  1201. [Fact]
  1202. public void Right_Click_On_UnselectedItem_Should_Clear_Existing_Selection()
  1203. {
  1204. using (Application())
  1205. {
  1206. var tree = CreateTestTreeData();
  1207. var target = new TreeView
  1208. {
  1209. Template = CreateTreeViewTemplate(),
  1210. Items = tree,
  1211. SelectionMode = SelectionMode.Multiple,
  1212. };
  1213. var visualRoot = new TestRoot();
  1214. visualRoot.Child = target;
  1215. CreateNodeDataTemplate(target);
  1216. ApplyTemplates(target);
  1217. target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
  1218. var rootNode = tree[0];
  1219. var to = rootNode.Children[0];
  1220. var then = rootNode.Children[1];
  1221. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(rootNode);
  1222. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  1223. var thenContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(then);
  1224. ClickContainer(fromContainer, KeyModifiers.None);
  1225. ClickContainer(toContainer, KeyModifiers.Shift);
  1226. Assert.Equal(2, target.SelectedItems.Count);
  1227. _mouse.Click(thenContainer, MouseButton.Right);
  1228. Assert.Equal(1, target.SelectedItems.Count);
  1229. }
  1230. }
  1231. [Fact]
  1232. public void Shift_Right_Click_Should_Not_Select_Multiple()
  1233. {
  1234. using (Application())
  1235. {
  1236. var tree = CreateTestTreeData();
  1237. var target = new TreeView
  1238. {
  1239. Template = CreateTreeViewTemplate(),
  1240. Items = tree,
  1241. SelectionMode = SelectionMode.Multiple,
  1242. };
  1243. var visualRoot = new TestRoot();
  1244. visualRoot.Child = target;
  1245. CreateNodeDataTemplate(target);
  1246. ApplyTemplates(target);
  1247. target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
  1248. var rootNode = tree[0];
  1249. var from = rootNode.Children[0];
  1250. var to = rootNode.Children[1];
  1251. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  1252. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  1253. _mouse.Click(fromContainer);
  1254. _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Shift);
  1255. Assert.Equal(1, target.SelectedItems.Count);
  1256. }
  1257. }
  1258. [Fact]
  1259. public void Ctrl_Right_Click_Should_Not_Select_Multiple()
  1260. {
  1261. using (Application())
  1262. {
  1263. var tree = CreateTestTreeData();
  1264. var target = new TreeView
  1265. {
  1266. Template = CreateTreeViewTemplate(),
  1267. Items = tree,
  1268. SelectionMode = SelectionMode.Multiple,
  1269. };
  1270. var visualRoot = new TestRoot();
  1271. visualRoot.Child = target;
  1272. CreateNodeDataTemplate(target);
  1273. ApplyTemplates(target);
  1274. target.ExpandSubTree((TreeViewItem)target.Presenter.Panel.Children[0]);
  1275. var rootNode = tree[0];
  1276. var from = rootNode.Children[0];
  1277. var to = rootNode.Children[1];
  1278. var fromContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(from);
  1279. var toContainer = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(to);
  1280. _mouse.Click(fromContainer);
  1281. _mouse.Click(toContainer, MouseButton.Right, modifiers: KeyModifiers.Control);
  1282. Assert.Equal(1, target.SelectedItems.Count);
  1283. }
  1284. }
  1285. [Fact]
  1286. public void TreeViewItems_Level_Should_Be_Set()
  1287. {
  1288. var tree = CreateTestTreeData();
  1289. var target = new TreeView
  1290. {
  1291. Template = CreateTreeViewTemplate(),
  1292. Items = tree,
  1293. };
  1294. var visualRoot = new TestRoot();
  1295. visualRoot.Child = target;
  1296. CreateNodeDataTemplate(target);
  1297. ApplyTemplates(target);
  1298. ExpandAll(target);
  1299. Assert.Equal(0, GetItem(target, 0).Level);
  1300. Assert.Equal(1, GetItem(target, 0, 0).Level);
  1301. Assert.Equal(1, GetItem(target, 0, 1).Level);
  1302. Assert.Equal(1, GetItem(target, 0, 2).Level);
  1303. Assert.Equal(2, GetItem(target, 0, 1, 0).Level);
  1304. }
  1305. [Fact]
  1306. public void TreeViewItems_Level_Should_Be_Set_For_Derived_TreeView()
  1307. {
  1308. var tree = CreateTestTreeData();
  1309. var target = new DerivedTreeView
  1310. {
  1311. Template = CreateTreeViewTemplate(),
  1312. Items = tree,
  1313. };
  1314. var visualRoot = new TestRoot();
  1315. visualRoot.Child = target;
  1316. CreateNodeDataTemplate(target);
  1317. ApplyTemplates(target);
  1318. ExpandAll(target);
  1319. Assert.Equal(0, GetItem(target, 0).Level);
  1320. Assert.Equal(1, GetItem(target, 0, 0).Level);
  1321. Assert.Equal(1, GetItem(target, 0, 1).Level);
  1322. Assert.Equal(1, GetItem(target, 0, 2).Level);
  1323. Assert.Equal(2, GetItem(target, 0, 1, 0).Level);
  1324. }
  1325. [Fact]
  1326. public void Adding_Node_To_Removed_And_ReAdded_Parent_Should_Not_Crash()
  1327. {
  1328. // Issue #2985
  1329. var tree = CreateTestTreeData();
  1330. var target = new TreeView
  1331. {
  1332. Template = CreateTreeViewTemplate(),
  1333. Items = tree,
  1334. };
  1335. var visualRoot = new TestRoot();
  1336. visualRoot.Child = target;
  1337. CreateNodeDataTemplate(target);
  1338. ApplyTemplates(target);
  1339. ExpandAll(target);
  1340. var parent = tree[0];
  1341. var node = parent.Children[1];
  1342. parent.Children.Remove(node);
  1343. parent.Children.Add(node);
  1344. var item = target.ItemContainerGenerator.Index.ContainerFromItem(node);
  1345. ApplyTemplates(new[] { item });
  1346. // #2985 causes ArgumentException here.
  1347. node.Children.Add(new Node());
  1348. }
  1349. [Fact]
  1350. public void Auto_Expanding_In_Style_Should_Not_Break_Range_Selection()
  1351. {
  1352. // Issue #2980.
  1353. using (Application())
  1354. {
  1355. var target = new DerivedTreeView
  1356. {
  1357. Template = CreateTreeViewTemplate(),
  1358. SelectionMode = SelectionMode.Multiple,
  1359. Items = new List<Node>
  1360. {
  1361. new Node { Value = "Root1", },
  1362. new Node { Value = "Root2", },
  1363. },
  1364. };
  1365. var visualRoot = new TestRoot
  1366. {
  1367. Styles =
  1368. {
  1369. new Style(x => x.OfType<TreeViewItem>())
  1370. {
  1371. Setters =
  1372. {
  1373. new Setter(TreeViewItem.IsExpandedProperty, true),
  1374. },
  1375. },
  1376. },
  1377. Child = target,
  1378. };
  1379. CreateNodeDataTemplate(target);
  1380. ApplyTemplates(target);
  1381. _mouse.Click(GetItem(target, 0));
  1382. _mouse.Click(GetItem(target, 1), modifiers: KeyModifiers.Shift);
  1383. }
  1384. }
  1385. [Fact]
  1386. public void Removing_TreeView_From_Root_Should_Preserve_TreeViewItems()
  1387. {
  1388. // Issue #3328
  1389. var tree = CreateTestTreeData();
  1390. var target = new TreeView
  1391. {
  1392. Template = CreateTreeViewTemplate(),
  1393. Items = tree,
  1394. };
  1395. var root = new TestRoot();
  1396. root.Child = target;
  1397. CreateNodeDataTemplate(target);
  1398. ApplyTemplates(target);
  1399. ExpandAll(target);
  1400. Assert.Equal(5, target.ItemContainerGenerator.Index.Containers.Count());
  1401. root.Child = null;
  1402. Assert.Equal(5, target.ItemContainerGenerator.Index.Containers.Count());
  1403. Assert.Equal(1, target.Presenter.Panel.Children.Count);
  1404. var rootNode = Assert.IsType<TreeViewItem>(target.Presenter.Panel.Children[0]);
  1405. Assert.Equal(3, rootNode.ItemContainerGenerator.Containers.Count());
  1406. Assert.Equal(3, rootNode.Presenter.Panel.Children.Count);
  1407. var child2Node = Assert.IsType<TreeViewItem>(rootNode.Presenter.Panel.Children[1]);
  1408. Assert.Equal(1, child2Node.ItemContainerGenerator.Containers.Count());
  1409. Assert.Equal(1, child2Node.Presenter.Panel.Children.Count);
  1410. }
  1411. [Fact]
  1412. public void Clearing_TreeView_Items_Clears_Index()
  1413. {
  1414. // Issue #3551
  1415. var tree = CreateTestTreeData();
  1416. var target = new TreeView
  1417. {
  1418. Template = CreateTreeViewTemplate(),
  1419. Items = tree,
  1420. };
  1421. var root = new TestRoot();
  1422. root.Child = target;
  1423. CreateNodeDataTemplate(target);
  1424. ApplyTemplates(target);
  1425. var rootNode = tree[0];
  1426. var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(rootNode);
  1427. Assert.NotNull(container);
  1428. root.Child = null;
  1429. tree.Clear();
  1430. Assert.Empty(target.ItemContainerGenerator.Index.Containers);
  1431. }
  1432. [Fact]
  1433. public void Can_Use_Derived_TreeViewItem()
  1434. {
  1435. var tree = CreateTestTreeData();
  1436. var target = new DerivedTreeViewWithDerivedTreeViewItems
  1437. {
  1438. Template = CreateTreeViewTemplate(),
  1439. Items = tree,
  1440. };
  1441. ApplyTemplates(target);
  1442. // Verify that all items are DerivedTreeViewItem
  1443. VerifyItemType(target.ItemContainerGenerator);
  1444. void VerifyItemType(ITreeItemContainerGenerator containerGenerator)
  1445. {
  1446. foreach (var container in containerGenerator.Index.Containers)
  1447. {
  1448. var item = Assert.IsType<DerivedTreeViewItem>(container);
  1449. if (item.ItemCount > 0)
  1450. {
  1451. VerifyItemType(item.ItemContainerGenerator);
  1452. }
  1453. }
  1454. }
  1455. }
  1456. private void ApplyTemplates(TreeView tree)
  1457. {
  1458. tree.ApplyTemplate();
  1459. tree.Presenter.ApplyTemplate();
  1460. ApplyTemplates(tree.Presenter.Panel.Children);
  1461. }
  1462. private void ApplyTemplates(IEnumerable<Control> controls)
  1463. {
  1464. foreach (TreeViewItem control in controls)
  1465. {
  1466. control.Template = CreateTreeViewItemTemplate();
  1467. control.ApplyTemplate();
  1468. control.Presenter.ApplyTemplate();
  1469. control.HeaderPresenter.ApplyTemplate();
  1470. ApplyTemplates(control.Presenter.Panel.Children);
  1471. }
  1472. }
  1473. private TreeViewItem GetItem(TreeView target, params int[] indexes)
  1474. {
  1475. var c = (ItemsControl)target;
  1476. foreach (var index in indexes)
  1477. {
  1478. var item = ((IList)c.Items)[index];
  1479. c = (ItemsControl)target.ItemContainerGenerator.Index.ContainerFromItem(item);
  1480. }
  1481. return (TreeViewItem)c;
  1482. }
  1483. private IList<Node> CreateTestTreeData()
  1484. {
  1485. return new AvaloniaList<Node>
  1486. {
  1487. new Node
  1488. {
  1489. Value = "Root",
  1490. Children = new AvaloniaList<Node>
  1491. {
  1492. new Node
  1493. {
  1494. Value = "Child1",
  1495. },
  1496. new Node
  1497. {
  1498. Value = "Child2",
  1499. Children = new AvaloniaList<Node>
  1500. {
  1501. new Node
  1502. {
  1503. Value = "Grandchild2a",
  1504. },
  1505. },
  1506. },
  1507. new Node
  1508. {
  1509. Value = "Child3",
  1510. }
  1511. }
  1512. }
  1513. };
  1514. }
  1515. private void CreateNodeDataTemplate(Control control)
  1516. {
  1517. control.DataTemplates.Add(new TestTreeDataTemplate());
  1518. }
  1519. private IControlTemplate CreateTreeViewTemplate()
  1520. {
  1521. return new FuncControlTemplate<TreeView>((parent, scope) => new ItemsPresenter
  1522. {
  1523. Name = "PART_ItemsPresenter",
  1524. [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
  1525. }.RegisterInNameScope(scope));
  1526. }
  1527. private IControlTemplate CreateTreeViewItemTemplate()
  1528. {
  1529. return new FuncControlTemplate<TreeViewItem>((parent, scope) => new Panel
  1530. {
  1531. Children =
  1532. {
  1533. new Border
  1534. {
  1535. Name = "PART_Header",
  1536. Child = new ContentPresenter
  1537. {
  1538. Name = "PART_HeaderPresenter",
  1539. [~ContentPresenter.ContentProperty] = parent[~TreeViewItem.HeaderProperty],
  1540. }.RegisterInNameScope(scope)
  1541. }.RegisterInNameScope(scope),
  1542. new ItemsPresenter
  1543. {
  1544. Name = "PART_ItemsPresenter",
  1545. [~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
  1546. }.RegisterInNameScope(scope)
  1547. }
  1548. });
  1549. }
  1550. private void ExpandAll(TreeView tree)
  1551. {
  1552. foreach (var i in tree.ItemContainerGenerator.Containers)
  1553. {
  1554. tree.ExpandSubTree((TreeViewItem)i.ContainerControl);
  1555. }
  1556. }
  1557. private void CollapseAll(TreeView tree)
  1558. {
  1559. foreach (var i in tree.ItemContainerGenerator.Containers)
  1560. {
  1561. tree.CollapseSubTree((TreeViewItem)i.ContainerControl);
  1562. }
  1563. }
  1564. private List<string> ExtractItemHeader(TreeView tree, int level)
  1565. {
  1566. return ExtractItemContent(tree.Presenter.Panel, 0, level)
  1567. .Select(x => x.Header)
  1568. .OfType<TextBlock>()
  1569. .Select(x => x.Text)
  1570. .ToList();
  1571. }
  1572. private IEnumerable<TreeViewItem> ExtractItemContent(Panel panel, int currentLevel, int level)
  1573. {
  1574. foreach (TreeViewItem container in panel.Children)
  1575. {
  1576. if (container.Template == null)
  1577. {
  1578. container.Template = CreateTreeViewItemTemplate();
  1579. container.ApplyTemplate();
  1580. }
  1581. if (currentLevel == level)
  1582. {
  1583. yield return container;
  1584. }
  1585. else
  1586. {
  1587. foreach (var child in ExtractItemContent(container.Presenter.Panel, currentLevel + 1, level))
  1588. {
  1589. yield return child;
  1590. }
  1591. }
  1592. }
  1593. }
  1594. private void ClickContainer(Control container, KeyModifiers modifiers)
  1595. {
  1596. _mouse.Click(container, modifiers: modifiers);
  1597. }
  1598. private void AssertChildrenSelected(TreeView treeView, Node rootNode)
  1599. {
  1600. foreach (var child in rootNode.Children)
  1601. {
  1602. var container = (TreeViewItem)treeView.ItemContainerGenerator.Index.ContainerFromItem(child);
  1603. Assert.True(container.IsSelected);
  1604. }
  1605. }
  1606. private IDisposable Application()
  1607. {
  1608. return UnitTestApplication.Start(
  1609. TestServices.MockThreadingInterface.With(
  1610. focusManager: new FocusManager(),
  1611. keyboardDevice: () => new KeyboardDevice(),
  1612. keyboardNavigation: new KeyboardNavigationHandler(),
  1613. inputManager: new InputManager()));
  1614. }
  1615. private class Node : NotifyingBase
  1616. {
  1617. private IAvaloniaList<Node> _children;
  1618. public string Value { get; set; }
  1619. public IAvaloniaList<Node> Children
  1620. {
  1621. get
  1622. {
  1623. return _children;
  1624. }
  1625. set
  1626. {
  1627. _children = value;
  1628. RaisePropertyChanged(nameof(Children));
  1629. }
  1630. }
  1631. }
  1632. private class TestTreeDataTemplate : ITreeDataTemplate
  1633. {
  1634. public Control Build(object param)
  1635. {
  1636. var node = (Node)param;
  1637. return new TextBlock { Text = node.Value };
  1638. }
  1639. public InstancedBinding ItemsSelector(object item)
  1640. {
  1641. var obs = ExpressionObserver.Create(item, o => (o as Node).Children);
  1642. return InstancedBinding.OneWay(obs);
  1643. }
  1644. public bool Match(object data)
  1645. {
  1646. return data is Node;
  1647. }
  1648. }
  1649. private class DerivedTreeView : TreeView
  1650. {
  1651. }
  1652. private class DerivedTreeViewWithDerivedTreeViewItems : TreeView
  1653. {
  1654. protected override ITreeItemContainerGenerator CreateTreeItemContainerGenerator() =>
  1655. CreateTreeItemContainerGenerator<DerivedTreeViewItem>();
  1656. }
  1657. private class DerivedTreeViewItem : TreeViewItem
  1658. {
  1659. protected override IItemContainerGenerator CreateItemContainerGenerator() => CreateTreeItemContainerGenerator<DerivedTreeViewItem>();
  1660. }
  1661. private class TestDataContext : INotifyPropertyChanged
  1662. {
  1663. private string _selectedItem;
  1664. public TestDataContext()
  1665. {
  1666. Items = new ObservableCollection<string>(Enumerable.Range(0, 5).Select(i => $"Item {i}"));
  1667. }
  1668. public ObservableCollection<string> Items { get; }
  1669. public string SelectedItem
  1670. {
  1671. get { return _selectedItem; }
  1672. set
  1673. {
  1674. _selectedItem = value;
  1675. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
  1676. }
  1677. }
  1678. public event PropertyChangedEventHandler PropertyChanged;
  1679. }
  1680. }
  1681. }