SelectionModelTests.cs 78 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384
  1. // This source file is adapted from the WinUI project.
  2. // (https://github.com/microsoft/microsoft-ui-xaml)
  3. //
  4. // Licensed to The Avalonia Project under MIT License, courtesy of The .NET Foundation.
  5. using System;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Collections.ObjectModel;
  9. using System.Collections.Specialized;
  10. using System.ComponentModel;
  11. using System.Linq;
  12. using System.Reactive.Linq;
  13. using Avalonia.Collections;
  14. using Avalonia.Diagnostics;
  15. using ReactiveUI;
  16. using Xunit;
  17. using Xunit.Abstractions;
  18. namespace Avalonia.Controls.UnitTests
  19. {
  20. public class SelectionModelTests
  21. {
  22. private readonly ITestOutputHelper _output;
  23. public SelectionModelTests(ITestOutputHelper output)
  24. {
  25. _output = output;
  26. }
  27. [Fact]
  28. public void ValidateOneLevelSingleSelectionNoSource()
  29. {
  30. SelectionModel selectionModel = new SelectionModel() { SingleSelect = true };
  31. _output.WriteLine("No source set.");
  32. Select(selectionModel, 4, true);
  33. ValidateSelection(selectionModel, new List<IndexPath>() { Path(4) });
  34. Select(selectionModel, 4, false);
  35. ValidateSelection(selectionModel, new List<IndexPath>() { });
  36. }
  37. [Fact]
  38. public void ValidateOneLevelSingleSelection()
  39. {
  40. SelectionModel selectionModel = new SelectionModel() { SingleSelect = true };
  41. _output.WriteLine("Set the source to 10 items");
  42. selectionModel.Source = Enumerable.Range(0, 10).ToList();
  43. Select(selectionModel, 3, true);
  44. ValidateSelection(selectionModel, new List<IndexPath>() { Path(3) }, new List<IndexPath>() { Path() });
  45. Select(selectionModel, 3, false);
  46. ValidateSelection(selectionModel, new List<IndexPath>() { });
  47. }
  48. [Fact]
  49. public void ValidateSelectionChangedEvent()
  50. {
  51. SelectionModel selectionModel = new SelectionModel();
  52. selectionModel.Source = Enumerable.Range(0, 10).ToList();
  53. int selectionChangedFiredCount = 0;
  54. selectionModel.SelectionChanged += delegate (object sender, SelectionModelSelectionChangedEventArgs args)
  55. {
  56. selectionChangedFiredCount++;
  57. ValidateSelection(selectionModel, new List<IndexPath>() { Path(4) }, new List<IndexPath>() { Path() });
  58. };
  59. Select(selectionModel, 4, true);
  60. ValidateSelection(selectionModel, new List<IndexPath>() { Path(4) }, new List<IndexPath>() { Path() });
  61. Assert.Equal(1, selectionChangedFiredCount);
  62. }
  63. [Fact]
  64. public void ValidateCanSetSelectedIndex()
  65. {
  66. var model = new SelectionModel();
  67. var ip = IndexPath.CreateFrom(34);
  68. model.SelectedIndex = ip;
  69. Assert.Equal(0, ip.CompareTo(model.SelectedIndex));
  70. }
  71. [Fact]
  72. public void ValidateOneLevelMultipleSelection()
  73. {
  74. SelectionModel selectionModel = new SelectionModel();
  75. selectionModel.Source = Enumerable.Range(0, 10).ToList();
  76. Select(selectionModel, 4, true);
  77. ValidateSelection(selectionModel, new List<IndexPath>() { Path(4) }, new List<IndexPath>() { Path() });
  78. SelectRangeFromAnchor(selectionModel, 8, true /* select */);
  79. ValidateSelection(selectionModel,
  80. new List<IndexPath>()
  81. {
  82. Path(4),
  83. Path(5),
  84. Path(6),
  85. Path(7),
  86. Path(8)
  87. },
  88. new List<IndexPath>() { Path() });
  89. ClearSelection(selectionModel);
  90. SetAnchorIndex(selectionModel, 6);
  91. SelectRangeFromAnchor(selectionModel, 3, true /* select */);
  92. ValidateSelection(selectionModel,
  93. new List<IndexPath>()
  94. {
  95. Path(3),
  96. Path(4),
  97. Path(5),
  98. Path(6)
  99. },
  100. new List<IndexPath>() { Path() });
  101. SetAnchorIndex(selectionModel, 4);
  102. SelectRangeFromAnchor(selectionModel, 5, false /* select */);
  103. ValidateSelection(selectionModel,
  104. new List<IndexPath>()
  105. {
  106. Path(3),
  107. Path(6)
  108. },
  109. new List<IndexPath>() { Path() });
  110. }
  111. [Fact]
  112. public void ValidateTwoLevelSingleSelection()
  113. {
  114. SelectionModel selectionModel = new SelectionModel();
  115. _output.WriteLine("Setting the source");
  116. selectionModel.Source = CreateNestedData(1 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */);
  117. Select(selectionModel, 1, 1, true);
  118. ValidateSelection(selectionModel,
  119. new List<IndexPath>() { Path(1, 1) }, new List<IndexPath>() { Path(), Path(1) });
  120. Select(selectionModel, 1, 1, false);
  121. ValidateSelection(selectionModel, new List<IndexPath>() { });
  122. }
  123. [Fact]
  124. public void ValidateTwoLevelMultipleSelection()
  125. {
  126. SelectionModel selectionModel = new SelectionModel();
  127. _output.WriteLine("Setting the source");
  128. selectionModel.Source = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */);
  129. Select(selectionModel, 1, 2, true);
  130. ValidateSelection(selectionModel, new List<IndexPath>() { Path(1, 2) }, new List<IndexPath>() { Path(), Path(1) });
  131. SelectRangeFromAnchor(selectionModel, 2, 2, true /* select */);
  132. ValidateSelection(selectionModel,
  133. new List<IndexPath>()
  134. {
  135. Path(1, 2),
  136. Path(2), // Inner node should be selected since everything 2.* is selected
  137. Path(2, 0),
  138. Path(2, 1),
  139. Path(2, 2)
  140. },
  141. new List<IndexPath>()
  142. {
  143. Path(),
  144. Path(1)
  145. },
  146. 1 /* selectedInnerNodes */);
  147. ClearSelection(selectionModel);
  148. SetAnchorIndex(selectionModel, 2, 1);
  149. SelectRangeFromAnchor(selectionModel, 0, 1, true /* select */);
  150. ValidateSelection(selectionModel,
  151. new List<IndexPath>()
  152. {
  153. Path(0, 1),
  154. Path(0, 2),
  155. Path(1, 0),
  156. Path(1, 1),
  157. Path(1, 2),
  158. Path(1),
  159. Path(2, 0),
  160. Path(2, 1)
  161. },
  162. new List<IndexPath>()
  163. {
  164. Path(),
  165. Path(0),
  166. Path(2),
  167. },
  168. 1 /* selectedInnerNodes */);
  169. SetAnchorIndex(selectionModel, 1, 1);
  170. SelectRangeFromAnchor(selectionModel, 2, 0, false /* select */);
  171. ValidateSelection(selectionModel,
  172. new List<IndexPath>()
  173. {
  174. Path(0, 1),
  175. Path(0, 2),
  176. Path(1, 0),
  177. Path(2, 1)
  178. },
  179. new List<IndexPath>()
  180. {
  181. Path(),
  182. Path(1),
  183. Path(0),
  184. Path(2),
  185. },
  186. 0 /* selectedInnerNodes */);
  187. ClearSelection(selectionModel);
  188. ValidateSelection(selectionModel, new List<IndexPath>() { });
  189. }
  190. [Fact]
  191. public void ValidateNestedSingleSelection()
  192. {
  193. SelectionModel selectionModel = new SelectionModel() { SingleSelect = true };
  194. _output.WriteLine("Setting the source");
  195. selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 2 /* countAtLeaf */);
  196. var path = Path(1, 0, 1, 1);
  197. Select(selectionModel, path, true);
  198. ValidateSelection(selectionModel,
  199. new List<IndexPath>() { path },
  200. new List<IndexPath>()
  201. {
  202. Path(),
  203. Path(1),
  204. Path(1, 0),
  205. Path(1, 0, 1),
  206. });
  207. Select(selectionModel, Path(0, 0, 1, 0), true);
  208. ValidateSelection(selectionModel,
  209. new List<IndexPath>()
  210. {
  211. Path(0, 0, 1, 0)
  212. },
  213. new List<IndexPath>()
  214. {
  215. Path(),
  216. Path(0),
  217. Path(0, 0),
  218. Path(0, 0, 1)
  219. });
  220. Select(selectionModel, Path(0, 0, 1, 0), false);
  221. ValidateSelection(selectionModel, new List<IndexPath>() { });
  222. }
  223. [Theory]
  224. [InlineData(true)]
  225. [InlineData(false)]
  226. public void ValidateNestedMultipleSelection(bool handleChildrenRequested)
  227. {
  228. SelectionModel selectionModel = new SelectionModel();
  229. List<IndexPath> sourcePaths = new List<IndexPath>();
  230. _output.WriteLine("Setting the source");
  231. selectionModel.Source = CreateNestedData(3 /* levels */ , 2 /* groupsAtLevel */, 4 /* countAtLeaf */);
  232. if (handleChildrenRequested)
  233. {
  234. selectionModel.ChildrenRequested += (object sender, SelectionModelChildrenRequestedEventArgs args) =>
  235. {
  236. _output.WriteLine("ChildrenRequestedIndexPath:" + args.SourceIndex);
  237. sourcePaths.Add(args.SourceIndex);
  238. args.Children = Observable.Return(args.Source as IEnumerable);
  239. };
  240. }
  241. var startPath = Path(1, 0, 1, 0);
  242. Select(selectionModel, startPath, true);
  243. ValidateSelection(selectionModel,
  244. new List<IndexPath>() { startPath },
  245. new List<IndexPath>()
  246. {
  247. Path(),
  248. Path(1),
  249. Path(1, 0),
  250. Path(1, 0, 1)
  251. });
  252. var endPath = Path(1, 1, 1, 0);
  253. SelectRangeFromAnchor(selectionModel, endPath, true /* select */);
  254. if (handleChildrenRequested)
  255. {
  256. // Validate SourceIndices.
  257. var expectedSourceIndices = new List<IndexPath>()
  258. {
  259. Path(1),
  260. Path(1, 0),
  261. Path(1, 0, 1),
  262. Path(1, 1),
  263. Path(1, 0, 1, 3),
  264. Path(1, 0, 1, 2),
  265. Path(1, 0, 1, 1),
  266. Path(1, 0, 1, 0),
  267. Path(1, 1, 1),
  268. Path(1, 1, 0),
  269. Path(1, 1, 0, 3),
  270. Path(1, 1, 0, 2),
  271. Path(1, 1, 0, 1),
  272. Path(1, 1, 0, 0),
  273. Path(1, 1, 1, 0)
  274. };
  275. Assert.Equal(expectedSourceIndices.Count, sourcePaths.Count);
  276. for (int i = 0; i < expectedSourceIndices.Count; i++)
  277. {
  278. Assert.True(AreEqual(expectedSourceIndices[i], sourcePaths[i]));
  279. }
  280. }
  281. ValidateSelection(selectionModel,
  282. new List<IndexPath>()
  283. {
  284. Path(1, 0),
  285. Path(1, 1),
  286. Path(1, 0, 1),
  287. Path(1, 0, 1, 0),
  288. Path(1, 0, 1, 1),
  289. Path(1, 0, 1, 2),
  290. Path(1, 0, 1, 3),
  291. Path(1, 1, 0),
  292. Path(1, 1, 1),
  293. Path(1, 1, 0, 0),
  294. Path(1, 1, 0, 1),
  295. Path(1, 1, 0, 2),
  296. Path(1, 1, 0, 3),
  297. Path(1, 1, 1, 0),
  298. },
  299. new List<IndexPath>()
  300. {
  301. Path(),
  302. Path(1),
  303. Path(1, 0),
  304. Path(1, 1),
  305. Path(1, 1, 1),
  306. });
  307. ClearSelection(selectionModel);
  308. ValidateSelection(selectionModel, new List<IndexPath>() { });
  309. startPath = Path(0, 1, 0, 2);
  310. SetAnchorIndex(selectionModel, startPath);
  311. endPath = Path(0, 0, 0, 2);
  312. SelectRangeFromAnchor(selectionModel, endPath, true /* select */);
  313. ValidateSelection(selectionModel,
  314. new List<IndexPath>()
  315. {
  316. Path(0, 0),
  317. Path(0, 1),
  318. Path(0, 0, 0),
  319. Path(0, 0, 1),
  320. Path(0, 0, 0, 2),
  321. Path(0, 0, 0, 3),
  322. Path(0, 0, 1, 0),
  323. Path(0, 0, 1, 1),
  324. Path(0, 0, 1, 2),
  325. Path(0, 0, 1, 3),
  326. Path(0, 1, 0),
  327. Path(0, 1, 0, 0),
  328. Path(0, 1, 0, 1),
  329. Path(0, 1, 0, 2),
  330. },
  331. new List<IndexPath>()
  332. {
  333. Path(),
  334. Path(0),
  335. Path(0, 0),
  336. Path(0, 0, 0),
  337. Path(0, 1),
  338. Path(0, 1, 0),
  339. });
  340. startPath = Path(0, 1, 0, 2);
  341. SetAnchorIndex(selectionModel, startPath);
  342. endPath = Path(0, 0, 0, 2);
  343. SelectRangeFromAnchor(selectionModel, endPath, false /* select */);
  344. ValidateSelection(selectionModel, new List<IndexPath>() { });
  345. }
  346. [Fact]
  347. public void ValidateInserts()
  348. {
  349. var data = new ObservableCollection<int>(Enumerable.Range(0, 10));
  350. var selectionModel = new SelectionModel();
  351. selectionModel.Source = data;
  352. selectionModel.Select(3);
  353. selectionModel.Select(4);
  354. selectionModel.Select(5);
  355. ValidateSelection(selectionModel,
  356. new List<IndexPath>()
  357. {
  358. Path(3),
  359. Path(4),
  360. Path(5),
  361. },
  362. new List<IndexPath>()
  363. {
  364. Path()
  365. });
  366. _output.WriteLine("Insert in selected range: Inserting 3 items at index 4");
  367. data.Insert(4, 41);
  368. data.Insert(4, 42);
  369. data.Insert(4, 43);
  370. ValidateSelection(selectionModel,
  371. new List<IndexPath>()
  372. {
  373. Path(3),
  374. Path(7),
  375. Path(8),
  376. },
  377. new List<IndexPath>()
  378. {
  379. Path()
  380. });
  381. _output.WriteLine("Insert before selected range: Inserting 3 items at index 0");
  382. data.Insert(0, 100);
  383. data.Insert(0, 101);
  384. data.Insert(0, 102);
  385. ValidateSelection(selectionModel,
  386. new List<IndexPath>()
  387. {
  388. Path(6),
  389. Path(10),
  390. Path(11),
  391. },
  392. new List<IndexPath>()
  393. {
  394. Path()
  395. });
  396. _output.WriteLine("Insert after selected range: Inserting 3 items at index 12");
  397. data.Insert(12, 1000);
  398. data.Insert(12, 1001);
  399. data.Insert(12, 1002);
  400. ValidateSelection(selectionModel,
  401. new List<IndexPath>()
  402. {
  403. Path(6),
  404. Path(10),
  405. Path(11),
  406. },
  407. new List<IndexPath>()
  408. {
  409. Path()
  410. });
  411. }
  412. [Fact]
  413. public void ValidateGroupInserts()
  414. {
  415. var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */);
  416. var selectionModel = new SelectionModel();
  417. selectionModel.Source = data;
  418. selectionModel.Select(1, 1);
  419. ValidateSelection(selectionModel,
  420. new List<IndexPath>()
  421. {
  422. Path(1, 1),
  423. },
  424. new List<IndexPath>()
  425. {
  426. Path(),
  427. Path(1),
  428. });
  429. _output.WriteLine("Insert before selected range: Inserting item at group index 0");
  430. data.Insert(0, 100);
  431. ValidateSelection(selectionModel,
  432. new List<IndexPath>()
  433. {
  434. Path(2, 1)
  435. },
  436. new List<IndexPath>()
  437. {
  438. Path(),
  439. Path(2),
  440. });
  441. _output.WriteLine("Insert after selected range: Inserting item at group index 3");
  442. data.Insert(3, 1000);
  443. ValidateSelection(selectionModel,
  444. new List<IndexPath>()
  445. {
  446. Path(2, 1)
  447. },
  448. new List<IndexPath>()
  449. {
  450. Path(),
  451. Path(2),
  452. });
  453. }
  454. [Fact]
  455. public void ValidateRemoves()
  456. {
  457. var data = new ObservableCollection<int>(Enumerable.Range(0, 10));
  458. var selectionModel = new SelectionModel();
  459. selectionModel.Source = data;
  460. selectionModel.Select(6);
  461. selectionModel.Select(7);
  462. selectionModel.Select(8);
  463. ValidateSelection(selectionModel,
  464. new List<IndexPath>()
  465. {
  466. Path(6),
  467. Path(7),
  468. Path(8)
  469. },
  470. new List<IndexPath>()
  471. {
  472. Path()
  473. });
  474. _output.WriteLine("Remove before selected range: Removing item at index 0");
  475. data.RemoveAt(0);
  476. ValidateSelection(selectionModel,
  477. new List<IndexPath>()
  478. {
  479. Path(5),
  480. Path(6),
  481. Path(7)
  482. },
  483. new List<IndexPath>()
  484. {
  485. Path()
  486. });
  487. _output.WriteLine("Remove from before to middle of selected range: Removing items at index 3, 4, 5");
  488. data.RemoveAt(3);
  489. data.RemoveAt(3);
  490. data.RemoveAt(3);
  491. ValidateSelection(selectionModel,
  492. new List<IndexPath>()
  493. {
  494. Path(3),
  495. Path(4)
  496. },
  497. new List<IndexPath>()
  498. {
  499. Path()
  500. });
  501. _output.WriteLine("Remove after selected range: Removing item at index 5");
  502. data.RemoveAt(5);
  503. ValidateSelection(selectionModel,
  504. new List<IndexPath>()
  505. {
  506. Path(3),
  507. Path(4)
  508. },
  509. new List<IndexPath>()
  510. {
  511. Path()
  512. });
  513. }
  514. [Fact]
  515. public void ValidateGroupRemoves()
  516. {
  517. var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */);
  518. var selectionModel = new SelectionModel();
  519. selectionModel.Source = data;
  520. selectionModel.Select(1, 1);
  521. selectionModel.Select(1, 2);
  522. ValidateSelection(selectionModel,
  523. new List<IndexPath>()
  524. {
  525. Path(1, 1),
  526. Path(1, 2)
  527. },
  528. new List<IndexPath>()
  529. {
  530. Path(),
  531. Path(1),
  532. });
  533. _output.WriteLine("Remove before selected range: Removing item at group index 0");
  534. data.RemoveAt(0);
  535. ValidateSelection(selectionModel,
  536. new List<IndexPath>()
  537. {
  538. Path(0, 1),
  539. Path(0, 2)
  540. },
  541. new List<IndexPath>()
  542. {
  543. Path(),
  544. Path(0),
  545. });
  546. _output.WriteLine("Remove after selected range: Removing item at group index 1");
  547. data.RemoveAt(1);
  548. ValidateSelection(selectionModel,
  549. new List<IndexPath>()
  550. {
  551. Path(0, 1),
  552. Path(0, 2)
  553. },
  554. new List<IndexPath>()
  555. {
  556. Path(),
  557. Path(0),
  558. });
  559. _output.WriteLine("Remove group containing selected items");
  560. data.RemoveAt(0);
  561. ValidateSelection(selectionModel, new List<IndexPath>());
  562. }
  563. [Fact]
  564. public void CanReplaceItem()
  565. {
  566. var data = new ObservableCollection<int>(Enumerable.Range(0, 10));
  567. var selectionModel = new SelectionModel();
  568. selectionModel.Source = data;
  569. selectionModel.Select(3);
  570. selectionModel.Select(4);
  571. selectionModel.Select(5);
  572. ValidateSelection(selectionModel,
  573. new List<IndexPath>()
  574. {
  575. Path(3),
  576. Path(4),
  577. Path(5),
  578. },
  579. new List<IndexPath>()
  580. {
  581. Path()
  582. });
  583. data[3] = 300;
  584. data[4] = 400;
  585. ValidateSelection(selectionModel,
  586. new List<IndexPath>()
  587. {
  588. Path(5),
  589. },
  590. new List<IndexPath>()
  591. {
  592. Path()
  593. });
  594. }
  595. [Fact]
  596. public void ValidateGroupReplaceLosesSelection()
  597. {
  598. var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */);
  599. var selectionModel = new SelectionModel();
  600. selectionModel.Source = data;
  601. selectionModel.Select(1, 1);
  602. ValidateSelection(selectionModel,
  603. new List<IndexPath>()
  604. {
  605. Path(1, 1)
  606. },
  607. new List<IndexPath>()
  608. {
  609. Path(),
  610. Path(1)
  611. });
  612. data[1] = new ObservableCollection<int>(Enumerable.Range(0, 5));
  613. ValidateSelection(selectionModel, new List<IndexPath>());
  614. }
  615. [Fact]
  616. public void ValidateClear()
  617. {
  618. var data = new ObservableCollection<int>(Enumerable.Range(0, 10));
  619. var selectionModel = new SelectionModel();
  620. selectionModel.Source = data;
  621. selectionModel.Select(3);
  622. selectionModel.Select(4);
  623. selectionModel.Select(5);
  624. ValidateSelection(selectionModel,
  625. new List<IndexPath>()
  626. {
  627. Path(3),
  628. Path(4),
  629. Path(5),
  630. },
  631. new List<IndexPath>()
  632. {
  633. Path()
  634. });
  635. data.Clear();
  636. ValidateSelection(selectionModel, new List<IndexPath>());
  637. }
  638. [Fact]
  639. public void ValidateGroupClear()
  640. {
  641. var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */);
  642. var selectionModel = new SelectionModel();
  643. selectionModel.Source = data;
  644. selectionModel.Select(1, 1);
  645. ValidateSelection(selectionModel,
  646. new List<IndexPath>()
  647. {
  648. Path(1, 1)
  649. },
  650. new List<IndexPath>()
  651. {
  652. Path(),
  653. Path(1)
  654. });
  655. (data[1] as IList).Clear();
  656. ValidateSelection(selectionModel, new List<IndexPath>());
  657. }
  658. // In some cases the leaf node might get a collection change that affects an ancestors selection
  659. // state. In this case we were not raising selection changed event. For example, if all elements
  660. // in a group are selected and a new item gets inserted - the parent goes from selected to partially
  661. // selected. In that case we need to raise the selection changed event so that the header containers
  662. // can show the correct visual.
  663. [Fact]
  664. public void ValidateEventWhenInnerNodeChangesSelectionState()
  665. {
  666. bool selectionChangedRaised = false;
  667. var data = CreateNestedData(1 /* levels */ , 3 /* groupsAtLevel */, 3 /* countAtLeaf */);
  668. var selectionModel = new SelectionModel();
  669. selectionModel.Source = data;
  670. selectionModel.SelectionChanged += (sender, args) => { selectionChangedRaised = true; };
  671. selectionModel.Select(1, 0);
  672. selectionModel.Select(1, 1);
  673. selectionModel.Select(1, 2);
  674. ValidateSelection(selectionModel,
  675. new List<IndexPath>()
  676. {
  677. Path(1, 0),
  678. Path(1, 1),
  679. Path(1, 2),
  680. Path(1)
  681. },
  682. new List<IndexPath>()
  683. {
  684. Path(),
  685. },
  686. 1 /* selectedInnerNodes */);
  687. _output.WriteLine("Inserting 1.0");
  688. selectionChangedRaised = false;
  689. (data[1] as AvaloniaList<object>).Insert(0, 100);
  690. Assert.True(selectionChangedRaised, "SelectionChanged event was not raised");
  691. ValidateSelection(selectionModel,
  692. new List<IndexPath>()
  693. {
  694. Path(1, 1),
  695. Path(1, 2),
  696. Path(1, 3),
  697. },
  698. new List<IndexPath>()
  699. {
  700. Path(),
  701. Path(1),
  702. });
  703. _output.WriteLine("Removing 1.0");
  704. selectionChangedRaised = false;
  705. (data[1] as AvaloniaList<object>).RemoveAt(0);
  706. Assert.True(selectionChangedRaised, "SelectionChanged event was not raised");
  707. ValidateSelection(selectionModel,
  708. new List<IndexPath>()
  709. {
  710. Path(1, 0),
  711. Path(1, 1),
  712. Path(1, 2),
  713. Path(1)
  714. },
  715. new List<IndexPath>()
  716. {
  717. Path(),
  718. },
  719. 1 /* selectedInnerNodes */);
  720. }
  721. [Fact]
  722. public void ValidatePropertyChangedEventIsRaised()
  723. {
  724. var selectionModel = new SelectionModel();
  725. _output.WriteLine("Set the source to 10 items");
  726. selectionModel.Source = Enumerable.Range(0, 10).ToList();
  727. bool selectedIndexChanged = false;
  728. bool selectedIndicesChanged = false;
  729. bool SelectedItemChanged = false;
  730. bool SelectedItemsChanged = false;
  731. bool AnchorIndexChanged = false;
  732. selectionModel.PropertyChanged += (sender, args) =>
  733. {
  734. switch (args.PropertyName)
  735. {
  736. case "SelectedIndex":
  737. selectedIndexChanged = true;
  738. break;
  739. case "SelectedIndices":
  740. selectedIndicesChanged = true;
  741. break;
  742. case "SelectedItem":
  743. SelectedItemChanged = true;
  744. break;
  745. case "SelectedItems":
  746. SelectedItemsChanged = true;
  747. break;
  748. case "AnchorIndex":
  749. AnchorIndexChanged = true;
  750. break;
  751. default:
  752. throw new InvalidOperationException();
  753. }
  754. };
  755. Select(selectionModel, 3, true);
  756. Assert.True(selectedIndexChanged);
  757. Assert.True(selectedIndicesChanged);
  758. Assert.True(SelectedItemChanged);
  759. Assert.True(SelectedItemsChanged);
  760. Assert.True(AnchorIndexChanged);
  761. }
  762. [Fact]
  763. public void CanExtendSelectionModelINPC()
  764. {
  765. var selectionModel = new CustomSelectionModel();
  766. bool intPropertyChanged = false;
  767. selectionModel.PropertyChanged += (sender, args) =>
  768. {
  769. if (args.PropertyName == "IntProperty")
  770. {
  771. intPropertyChanged = true;
  772. }
  773. };
  774. selectionModel.IntProperty = 5;
  775. Assert.True(intPropertyChanged);
  776. }
  777. [Fact]
  778. public void SelectRangeRegressionTest()
  779. {
  780. var selectionModel = new SelectionModel()
  781. {
  782. Source = CreateNestedData(1, 2, 3)
  783. };
  784. // length of start smaller than end used to cause an out of range error.
  785. selectionModel.SelectRange(IndexPath.CreateFrom(0), IndexPath.CreateFrom(1, 1));
  786. ValidateSelection(selectionModel,
  787. new List<IndexPath>()
  788. {
  789. Path(0),
  790. Path(1),
  791. Path(0, 0),
  792. Path(0, 1),
  793. Path(0, 2),
  794. Path(1, 0),
  795. Path(1, 1)
  796. },
  797. new List<IndexPath>()
  798. {
  799. Path(),
  800. Path(1)
  801. });
  802. }
  803. [Fact]
  804. public void Should_Listen_For_Changes_After_Deselect()
  805. {
  806. var target = new SelectionModel();
  807. var data = CreateNestedData(1, 2, 3);
  808. target.Source = data;
  809. target.Select(1, 0);
  810. target.Deselect(1, 0);
  811. target.Select(1, 0);
  812. ((AvaloniaList<object>)data[1]).Insert(0, "foo");
  813. Assert.Equal(new IndexPath(1, 1), target.SelectedIndex);
  814. }
  815. [Fact]
  816. public void Selecting_Item_Raises_SelectionChanged()
  817. {
  818. var target = new SelectionModel();
  819. var raised = 0;
  820. target.Source = Enumerable.Range(0, 10).ToList();
  821. target.SelectionChanged += (s, e) =>
  822. {
  823. Assert.Empty(e.DeselectedIndices);
  824. Assert.Empty(e.DeselectedItems);
  825. Assert.Equal(new[] { new IndexPath(4) }, e.SelectedIndices);
  826. Assert.Equal(new object[] { 4 }, e.SelectedItems);
  827. ++raised;
  828. };
  829. target.Select(4);
  830. Assert.Equal(1, raised);
  831. }
  832. [Fact]
  833. public void Selecting_Already_Selected_Item_Doesnt_Raise_SelectionChanged()
  834. {
  835. var target = new SelectionModel();
  836. var raised = 0;
  837. target.Source = Enumerable.Range(0, 10).ToList();
  838. target.Select(4);
  839. target.SelectionChanged += (s, e) => ++raised;
  840. target.Select(4);
  841. Assert.Equal(0, raised);
  842. }
  843. [Fact]
  844. public void SingleSelecting_Item_Raises_SelectionChanged()
  845. {
  846. var target = new SelectionModel { SingleSelect = true };
  847. var raised = 0;
  848. target.Source = Enumerable.Range(0, 10).ToList();
  849. target.Select(3);
  850. target.SelectionChanged += (s, e) =>
  851. {
  852. Assert.Equal(new[] { new IndexPath(3) }, e.DeselectedIndices);
  853. Assert.Equal(new object[] { 3 }, e.DeselectedItems);
  854. Assert.Equal(new[] { new IndexPath(4) }, e.SelectedIndices);
  855. Assert.Equal(new object[] { 4 }, e.SelectedItems);
  856. ++raised;
  857. };
  858. target.Select(4);
  859. Assert.Equal(1, raised);
  860. }
  861. [Fact]
  862. public void SingleSelecting_Already_Selected_Item_Doesnt_Raise_SelectionChanged()
  863. {
  864. var target = new SelectionModel { SingleSelect = true };
  865. var raised = 0;
  866. target.Source = Enumerable.Range(0, 10).ToList();
  867. target.Select(4);
  868. target.SelectionChanged += (s, e) => ++raised;
  869. target.Select(4);
  870. Assert.Equal(0, raised);
  871. }
  872. [Fact]
  873. public void Selecting_Item_With_Group_Raises_SelectionChanged()
  874. {
  875. var target = new SelectionModel();
  876. var raised = 0;
  877. target.Source = CreateNestedData(1, 2, 3);
  878. target.SelectionChanged += (s, e) =>
  879. {
  880. Assert.Empty(e.DeselectedIndices);
  881. Assert.Empty(e.DeselectedItems);
  882. Assert.Equal(new[] { new IndexPath(1, 1) }, e.SelectedIndices);
  883. Assert.Equal(new object[] { 4 }, e.SelectedItems);
  884. ++raised;
  885. };
  886. target.Select(1, 1);
  887. Assert.Equal(1, raised);
  888. }
  889. [Fact]
  890. public void SelectAt_Raises_SelectionChanged()
  891. {
  892. var target = new SelectionModel();
  893. var raised = 0;
  894. target.Source = CreateNestedData(1, 2, 3);
  895. target.SelectionChanged += (s, e) =>
  896. {
  897. Assert.Empty(e.DeselectedIndices);
  898. Assert.Empty(e.DeselectedItems);
  899. Assert.Equal(new[] { new IndexPath(1, 1) }, e.SelectedIndices);
  900. Assert.Equal(new object[] { 4 }, e.SelectedItems);
  901. ++raised;
  902. };
  903. target.SelectAt(new IndexPath(1, 1));
  904. Assert.Equal(1, raised);
  905. }
  906. [Fact]
  907. public void SelectAll_Raises_SelectionChanged()
  908. {
  909. var target = new SelectionModel { SingleSelect = true };
  910. var raised = 0;
  911. target.Source = Enumerable.Range(0, 10).ToList();
  912. target.SelectionChanged += (s, e) =>
  913. {
  914. var expected = Enumerable.Range(0, 10);
  915. Assert.Empty(e.DeselectedIndices);
  916. Assert.Empty(e.DeselectedItems);
  917. Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices);
  918. Assert.Equal(expected, e.SelectedItems.Cast<int>());
  919. ++raised;
  920. };
  921. target.SelectAll();
  922. Assert.Equal(1, raised);
  923. }
  924. [Fact]
  925. public void SelectAll_With_Already_Selected_Items_Raises_SelectionChanged()
  926. {
  927. var target = new SelectionModel { SingleSelect = true };
  928. var raised = 0;
  929. target.Source = Enumerable.Range(0, 10).ToList();
  930. target.Select(4);
  931. target.SelectionChanged += (s, e) =>
  932. {
  933. var expected = Enumerable.Range(0, 10).Except(new[] { 4 });
  934. Assert.Empty(e.DeselectedIndices);
  935. Assert.Empty(e.DeselectedItems);
  936. Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices);
  937. Assert.Equal(expected, e.SelectedItems.Cast<int>());
  938. ++raised;
  939. };
  940. target.SelectAll();
  941. Assert.Equal(1, raised);
  942. }
  943. [Fact]
  944. public void SelectRangeFromAnchor_Raises_SelectionChanged()
  945. {
  946. var target = new SelectionModel();
  947. var raised = 0;
  948. target.Source = Enumerable.Range(0, 10).ToList();
  949. target.SelectionChanged += (s, e) =>
  950. {
  951. var expected = Enumerable.Range(4, 3);
  952. Assert.Empty(e.DeselectedIndices);
  953. Assert.Empty(e.DeselectedItems);
  954. Assert.Equal(expected.Select(x => new IndexPath(x)), e.SelectedIndices);
  955. Assert.Equal(expected, e.SelectedItems.Cast<int>());
  956. ++raised;
  957. };
  958. target.AnchorIndex = new IndexPath(4);
  959. target.SelectRangeFromAnchor(6);
  960. Assert.Equal(1, raised);
  961. }
  962. [Fact]
  963. public void SelectRangeFromAnchor_With_Group_Raises_SelectionChanged()
  964. {
  965. var target = new SelectionModel();
  966. var raised = 0;
  967. target.Source = CreateNestedData(1, 2, 10);
  968. target.SelectionChanged += (s, e) =>
  969. {
  970. var expected = Enumerable.Range(11, 6);
  971. Assert.Empty(e.DeselectedIndices);
  972. Assert.Empty(e.DeselectedItems);
  973. Assert.Equal(expected.Select(x => new IndexPath(x / 10, x % 10)), e.SelectedIndices);
  974. Assert.Equal(expected, e.SelectedItems.Cast<int>());
  975. ++raised;
  976. };
  977. target.AnchorIndex = new IndexPath(1, 1);
  978. target.SelectRangeFromAnchor(1, 6);
  979. Assert.Equal(1, raised);
  980. }
  981. [Fact]
  982. public void SelectRangeFromAnchorTo_Raises_SelectionChanged()
  983. {
  984. var target = new SelectionModel();
  985. var raised = 0;
  986. target.Source = CreateNestedData(1, 2, 10);
  987. target.SelectionChanged += (s, e) =>
  988. {
  989. var expected = Enumerable.Range(11, 6);
  990. Assert.Empty(e.DeselectedIndices);
  991. Assert.Empty(e.DeselectedItems);
  992. Assert.Equal(expected.Select(x => new IndexPath(x / 10, x % 10)), e.SelectedIndices);
  993. Assert.Equal(expected, e.SelectedItems.Cast<int>());
  994. ++raised;
  995. };
  996. target.AnchorIndex = new IndexPath(1, 1);
  997. target.SelectRangeFromAnchorTo(new IndexPath(1, 6));
  998. Assert.Equal(1, raised);
  999. }
  1000. [Fact]
  1001. public void ClearSelection_Raises_SelectionChanged()
  1002. {
  1003. var target = new SelectionModel();
  1004. var raised = 0;
  1005. target.Source = Enumerable.Range(0, 10).ToList();
  1006. target.Select(4);
  1007. target.Select(5);
  1008. target.SelectionChanged += (s, e) =>
  1009. {
  1010. var expected = Enumerable.Range(4, 2);
  1011. Assert.Equal(expected.Select(x => new IndexPath(x)), e.DeselectedIndices);
  1012. Assert.Equal(expected, e.DeselectedItems.Cast<int>());
  1013. Assert.Empty(e.SelectedIndices);
  1014. Assert.Empty(e.SelectedItems);
  1015. ++raised;
  1016. };
  1017. target.ClearSelection();
  1018. Assert.Equal(1, raised);
  1019. }
  1020. [Fact]
  1021. public void Clearing_Nested_Selection_Raises_SelectionChanged()
  1022. {
  1023. var target = new SelectionModel();
  1024. var raised = 0;
  1025. target.Source = CreateNestedData(1, 2, 3);
  1026. target.Select(1, 1);
  1027. target.SelectionChanged += (s, e) =>
  1028. {
  1029. Assert.Equal(new[] { new IndexPath(1, 1) }, e.DeselectedIndices);
  1030. Assert.Equal(new object[] { 4 }, e.DeselectedItems);
  1031. Assert.Empty(e.SelectedIndices);
  1032. Assert.Empty(e.SelectedItems);
  1033. ++raised;
  1034. };
  1035. target.ClearSelection();
  1036. Assert.Equal(1, raised);
  1037. }
  1038. [Fact]
  1039. public void Changing_Source_Raises_SelectionChanged()
  1040. {
  1041. var target = new SelectionModel();
  1042. var raised = 0;
  1043. target.Source = Enumerable.Range(0, 10).ToList();
  1044. target.Select(4);
  1045. target.Select(5);
  1046. target.SelectionChanged += (s, e) =>
  1047. {
  1048. var expected = Enumerable.Range(4, 2);
  1049. Assert.Equal(expected.Select(x => new IndexPath(x)), e.DeselectedIndices);
  1050. Assert.Equal(expected, e.DeselectedItems.Cast<int>());
  1051. Assert.Empty(e.SelectedIndices);
  1052. Assert.Empty(e.SelectedItems);
  1053. ++raised;
  1054. };
  1055. target.Source = Enumerable.Range(20, 10).ToList();
  1056. Assert.Equal(1, raised);
  1057. }
  1058. [Fact]
  1059. public void Setting_SelectedIndex_Raises_SelectionChanged()
  1060. {
  1061. var target = new SelectionModel();
  1062. var raised = 0;
  1063. target.Source = Enumerable.Range(0, 10).ToList();
  1064. target.Select(4);
  1065. target.Select(5);
  1066. target.SelectionChanged += (s, e) =>
  1067. {
  1068. Assert.Equal(new[] { new IndexPath(4), new IndexPath(5) }, e.DeselectedIndices);
  1069. Assert.Equal(new object[] { 4, 5 }, e.DeselectedItems);
  1070. Assert.Equal(new[] { new IndexPath(6) }, e.SelectedIndices);
  1071. Assert.Equal(new object[] { 6 }, e.SelectedItems);
  1072. ++raised;
  1073. };
  1074. target.SelectedIndex = new IndexPath(6);
  1075. Assert.Equal(1, raised);
  1076. }
  1077. [Fact]
  1078. public void Removing_Selected_Item_Raises_SelectionChanged()
  1079. {
  1080. var target = new SelectionModel();
  1081. var data = new ObservableCollection<int>(Enumerable.Range(0, 10));
  1082. var raised = 0;
  1083. target.Source = data;
  1084. target.Select(4);
  1085. target.Select(5);
  1086. target.SelectionChanged += (s, e) =>
  1087. {
  1088. Assert.Empty(e.DeselectedIndices);
  1089. Assert.Equal(new object[] { 4 }, e.DeselectedItems);
  1090. Assert.Empty(e.SelectedIndices);
  1091. Assert.Empty(e.SelectedItems);
  1092. ++raised;
  1093. };
  1094. data.Remove(4);
  1095. Assert.Equal(1, raised);
  1096. }
  1097. [Fact]
  1098. public void Removing_Selected_Child_Item_Raises_SelectionChanged()
  1099. {
  1100. var target = new SelectionModel();
  1101. var data = CreateNestedData(1, 2, 3);
  1102. var raised = 0;
  1103. target.Source = data;
  1104. target.SelectRange(new IndexPath(0), new IndexPath(1, 1));
  1105. target.SelectionChanged += (s, e) =>
  1106. {
  1107. Assert.Empty(e.DeselectedIndices);
  1108. Assert.Equal(new object[] { 1}, e.DeselectedItems);
  1109. Assert.Empty(e.SelectedIndices);
  1110. Assert.Empty(e.SelectedItems);
  1111. ++raised;
  1112. };
  1113. ((AvaloniaList<object>)data[0]).RemoveAt(1);
  1114. Assert.Equal(1, raised);
  1115. }
  1116. [Fact]
  1117. public void Removing_Selected_Item_With_Children_Raises_SelectionChanged()
  1118. {
  1119. var target = new SelectionModel();
  1120. var data = CreateNestedData(1, 2, 3);
  1121. var raised = 0;
  1122. target.Source = data;
  1123. target.SelectRange(new IndexPath(0), new IndexPath(1, 1));
  1124. target.SelectionChanged += (s, e) =>
  1125. {
  1126. Assert.Empty(e.DeselectedIndices);
  1127. Assert.Equal(new object[] { new AvaloniaList<int> { 0, 1, 2 }, 0, 1, 2 }, e.DeselectedItems);
  1128. Assert.Empty(e.SelectedIndices);
  1129. Assert.Empty(e.SelectedItems);
  1130. ++raised;
  1131. };
  1132. data.RemoveAt(0);
  1133. Assert.Equal(1, raised);
  1134. }
  1135. [Fact]
  1136. public void Removing_Unselected_Item_Before_Selected_Item_Raises_SelectionChanged()
  1137. {
  1138. var target = new SelectionModel();
  1139. var data = new ObservableCollection<int>(Enumerable.Range(0, 10));
  1140. var raised = 0;
  1141. target.Source = data;
  1142. target.Select(8);
  1143. target.SelectionChanged += (s, e) =>
  1144. {
  1145. Assert.Empty(e.DeselectedIndices);
  1146. Assert.Empty(e.DeselectedItems);
  1147. Assert.Empty(e.SelectedIndices);
  1148. Assert.Empty(e.SelectedItems);
  1149. ++raised;
  1150. };
  1151. data.Remove(6);
  1152. Assert.Equal(1, raised);
  1153. }
  1154. [Fact]
  1155. public void Removing_Unselected_Item_After_Selected_Item_Doesnt_Raise_SelectionChanged()
  1156. {
  1157. var target = new SelectionModel();
  1158. var data = new ObservableCollection<int>(Enumerable.Range(0, 10));
  1159. var raised = 0;
  1160. target.Source = data;
  1161. target.Select(4);
  1162. target.SelectionChanged += (s, e) => ++raised;
  1163. data.Remove(6);
  1164. Assert.Equal(0, raised);
  1165. }
  1166. [Fact]
  1167. public void Disposing_Unhooks_CollectionChanged_Handlers()
  1168. {
  1169. var data = CreateNestedData(2, 2, 2);
  1170. var target = new SelectionModel { Source = data };
  1171. target.SelectAll();
  1172. VerifyCollectionChangedHandlers(1, data);
  1173. target.Dispose();
  1174. VerifyCollectionChangedHandlers(0, data);
  1175. }
  1176. [Fact]
  1177. public void Clearing_Selection_Unhooks_CollectionChanged_Handlers()
  1178. {
  1179. var data = CreateNestedData(2, 2, 2);
  1180. var target = new SelectionModel { Source = data };
  1181. target.SelectAll();
  1182. VerifyCollectionChangedHandlers(1, data);
  1183. target.ClearSelection();
  1184. // Root subscription not unhooked until SelectionModel is disposed.
  1185. Assert.Equal(1, GetSubscriberCount(data));
  1186. foreach (AvaloniaList<object> i in data)
  1187. {
  1188. VerifyCollectionChangedHandlers(0, i);
  1189. }
  1190. }
  1191. [Fact]
  1192. public void Removing_Item_Unhooks_CollectionChanged_Handlers()
  1193. {
  1194. var data = CreateNestedData(2, 2, 2);
  1195. var target = new SelectionModel { Source = data };
  1196. target.SelectAll();
  1197. var toRemove = (AvaloniaList<object>)data[1];
  1198. data.Remove(toRemove);
  1199. Assert.Equal(0, GetSubscriberCount(toRemove));
  1200. }
  1201. [Fact]
  1202. public void SelectRange_Behaves_The_Same_As_Multiple_Selects()
  1203. {
  1204. var data = new[] { 1, 2, 3 };
  1205. var target = new SelectionModel { Source = data };
  1206. target.Select(1);
  1207. Assert.Equal(new[] { IndexPath.CreateFrom(1) }, target.SelectedIndices);
  1208. target.ClearSelection();
  1209. target.SelectRange(new IndexPath(1), new IndexPath(1));
  1210. Assert.Equal(new[] { IndexPath.CreateFrom(1) }, target.SelectedIndices);
  1211. }
  1212. [Fact]
  1213. public void SelectRange_Behaves_The_Same_As_Multiple_Selects_Nested()
  1214. {
  1215. var data = CreateNestedData(3, 2, 2);
  1216. var target = new SelectionModel { Source = data };
  1217. target.Select(1);
  1218. Assert.Equal(new[] { IndexPath.CreateFrom(1) }, target.SelectedIndices);
  1219. target.ClearSelection();
  1220. target.SelectRange(new IndexPath(1), new IndexPath(1));
  1221. Assert.Equal(new[] { IndexPath.CreateFrom(1) }, target.SelectedIndices);
  1222. }
  1223. [Fact]
  1224. public void Should_Not_Treat_Strings_As_Nested_Selections()
  1225. {
  1226. var data = new[] { "foo", "bar", "baz" };
  1227. var target = new SelectionModel { Source = data };
  1228. target.SelectAll();
  1229. Assert.Equal(3, target.SelectedItems.Count);
  1230. }
  1231. [Fact]
  1232. public void Not_Enumerating_Changes_Does_Not_Prevent_Further_Operations()
  1233. {
  1234. var data = new[] { "foo", "bar", "baz" };
  1235. var target = new SelectionModel { Source = data };
  1236. target.SelectionChanged += (s, e) => { };
  1237. target.SelectAll();
  1238. target.ClearSelection();
  1239. }
  1240. [Fact]
  1241. public void Can_Change_Selection_From_SelectionChanged()
  1242. {
  1243. var data = new[] { "foo", "bar", "baz" };
  1244. var target = new SelectionModel { Source = data };
  1245. var raised = 0;
  1246. target.SelectionChanged += (s, e) =>
  1247. {
  1248. if (raised++ == 0)
  1249. {
  1250. target.ClearSelection();
  1251. }
  1252. };
  1253. target.SelectAll();
  1254. Assert.Equal(2, raised);
  1255. }
  1256. [Fact]
  1257. public void Raises_SelectionChanged_With_No_Source()
  1258. {
  1259. var target = new SelectionModel();
  1260. var raised = 0;
  1261. target.SelectionChanged += (s, e) =>
  1262. {
  1263. Assert.Empty(e.DeselectedIndices);
  1264. Assert.Empty(e.DeselectedItems);
  1265. Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices);
  1266. Assert.Empty(e.SelectedItems);
  1267. ++raised;
  1268. };
  1269. target.Select(1);
  1270. Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices);
  1271. Assert.Empty(target.SelectedItems);
  1272. }
  1273. [Fact]
  1274. public void Raises_SelectionChanged_With_Items_After_Source_Is_Set()
  1275. {
  1276. var target = new SelectionModel();
  1277. var raised = 0;
  1278. target.Select(1);
  1279. target.SelectionChanged += (s, e) =>
  1280. {
  1281. Assert.Empty(e.DeselectedIndices);
  1282. Assert.Empty(e.DeselectedItems);
  1283. Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices);
  1284. Assert.Equal(new[] { "bar" }, e.SelectedItems);
  1285. ++raised;
  1286. };
  1287. target.Source = new[] { "foo", "bar", "baz" };
  1288. Assert.Equal(1, raised);
  1289. }
  1290. [Fact]
  1291. public void RetainSelectionOnReset_Retains_Selection_On_Reset()
  1292. {
  1293. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1294. var target = new SelectionModel { Source = data, RetainSelectionOnReset = true };
  1295. target.SelectRange(new IndexPath(1), new IndexPath(2));
  1296. data.Reset();
  1297. Assert.Equal(new[] { new IndexPath(1), new IndexPath(2) }, target.SelectedIndices);
  1298. Assert.Equal(new[] { "bar", "baz" }, target.SelectedItems);
  1299. }
  1300. [Fact]
  1301. public void RetainSelectionOnReset_Retains_Correct_Selection_After_Deselect()
  1302. {
  1303. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1304. var target = new SelectionModel { Source = data, RetainSelectionOnReset = true };
  1305. target.SelectRange(new IndexPath(1), new IndexPath(2));
  1306. target.Deselect(2);
  1307. data.Reset();
  1308. Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices);
  1309. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  1310. }
  1311. [Fact]
  1312. public void RetainSelectionOnReset_Retains_Correct_Selection_After_Remove_1()
  1313. {
  1314. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1315. var target = new SelectionModel { Source = data, RetainSelectionOnReset = true };
  1316. target.SelectRange(new IndexPath(1), new IndexPath(2));
  1317. data.RemoveAt(2);
  1318. data.Reset(new[] { "foo", "bar", "baz" });
  1319. Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices);
  1320. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  1321. }
  1322. [Fact]
  1323. public void RetainSelectionOnReset_Retains_Correct_Selection_After_Remove_2()
  1324. {
  1325. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1326. var target = new SelectionModel { Source = data, RetainSelectionOnReset = true };
  1327. target.SelectRange(new IndexPath(1), new IndexPath(2));
  1328. data.RemoveAt(0);
  1329. data.Reset(new[] { "foo", "bar", "baz" });
  1330. Assert.Equal(new[] { new IndexPath(1), new IndexPath(2) }, target.SelectedIndices);
  1331. Assert.Equal(new[] { "bar", "baz" }, target.SelectedItems);
  1332. }
  1333. [Fact]
  1334. public void RetainSelectionOnReset_Retains_No_Selection_After_Clear()
  1335. {
  1336. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1337. var target = new SelectionModel { Source = data, RetainSelectionOnReset = true };
  1338. target.SelectRange(new IndexPath(1), new IndexPath(2));
  1339. target.ClearSelection();
  1340. data.Reset();
  1341. Assert.Empty(target.SelectedIndices);
  1342. Assert.Empty(target.SelectedItems);
  1343. }
  1344. [Fact]
  1345. public void RetainSelectionOnReset_Retains_Correct_Selection_After_Two_Resets()
  1346. {
  1347. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1348. var target = new SelectionModel { Source = data, RetainSelectionOnReset = true };
  1349. target.SelectRange(new IndexPath(1), new IndexPath(2));
  1350. data.Reset(new[] { "foo", "bar" });
  1351. data.Reset(new[] { "foo", "bar", "baz" });
  1352. Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices);
  1353. Assert.Equal(new[] { "bar", }, target.SelectedItems);
  1354. }
  1355. [Fact]
  1356. public void RetainSelectionOnReset_Raises_Empty_SelectionChanged_On_Reset_With_No_Changes()
  1357. {
  1358. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1359. var target = new SelectionModel { Source = data, RetainSelectionOnReset = true };
  1360. var raised = 0;
  1361. target.SelectRange(new IndexPath(1), new IndexPath(2));
  1362. target.SelectionChanged += (s, e) =>
  1363. {
  1364. Assert.Empty(e.DeselectedIndices);
  1365. Assert.Empty(e.DeselectedItems);
  1366. Assert.Empty(e.SelectedIndices);
  1367. Assert.Empty(e.SelectedItems);
  1368. ++raised;
  1369. };
  1370. data.Reset();
  1371. }
  1372. [Fact]
  1373. public void RetainSelectionOnReset_Raises_SelectionChanged_On_Reset_With_Removed_Items()
  1374. {
  1375. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1376. var target = new SelectionModel { Source = data, RetainSelectionOnReset = true };
  1377. var raised = 0;
  1378. target.SelectRange(new IndexPath(1), new IndexPath(2));
  1379. target.SelectionChanged += (s, e) =>
  1380. {
  1381. Assert.Empty(e.DeselectedIndices);
  1382. Assert.Equal(new[] { "bar" }, e.DeselectedItems);
  1383. Assert.Empty(e.SelectedIndices);
  1384. Assert.Empty(e.SelectedItems);
  1385. ++raised;
  1386. };
  1387. data.Reset(new[] { "foo", "baz" });
  1388. Assert.Equal(1, raised);
  1389. }
  1390. [Fact]
  1391. public void RetainSelectionOnReset_Handles_Null_Source()
  1392. {
  1393. var data = new ResettingList<string> { "foo", "bar", "baz" };
  1394. var target = new SelectionModel { RetainSelectionOnReset = true };
  1395. var raised = 0;
  1396. target.SelectionChanged += (s, e) =>
  1397. {
  1398. if (raised == 0)
  1399. {
  1400. Assert.Empty(e.DeselectedIndices);
  1401. Assert.Empty(e.DeselectedItems);
  1402. Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices);
  1403. Assert.Empty(e.SelectedItems);
  1404. }
  1405. else if (raised == 1)
  1406. {
  1407. Assert.Empty(e.DeselectedIndices);
  1408. Assert.Empty(e.DeselectedItems);
  1409. Assert.Equal(new[] { new IndexPath(1) }, e.SelectedIndices);
  1410. Assert.Equal(new[] { "bar" }, e.SelectedItems);
  1411. }
  1412. else if (raised == 3)
  1413. {
  1414. Assert.Empty(e.DeselectedIndices);
  1415. Assert.Empty(e.DeselectedItems);
  1416. Assert.Empty(e.SelectedIndices);
  1417. Assert.Empty(e.SelectedItems);
  1418. }
  1419. ++raised;
  1420. };
  1421. target.Select(1);
  1422. Assert.Equal(1, raised);
  1423. target.Source = data;
  1424. Assert.Equal(2, raised);
  1425. Assert.Equal(new[] { new IndexPath(1) }, target.SelectedIndices);
  1426. data.Reset(new[] { "qux", "foo", "bar", "baz" });
  1427. Assert.Equal(3, raised);
  1428. Assert.Equal(new[] { new IndexPath(2) }, target.SelectedIndices);
  1429. }
  1430. [Fact]
  1431. public void Can_Batch_Update()
  1432. {
  1433. var target = new SelectionModel();
  1434. var raised = 0;
  1435. target.Source = Enumerable.Range(0, 10).ToList();
  1436. target.Select(1);
  1437. target.SelectionChanged += (s, e) =>
  1438. {
  1439. Assert.Equal(new[] { new IndexPath(1) }, e.DeselectedIndices);
  1440. Assert.Equal(new object[] { 1 }, e.DeselectedItems);
  1441. Assert.Equal(new[] { new IndexPath(4) }, e.SelectedIndices);
  1442. Assert.Equal(new object[] { 4 }, e.SelectedItems);
  1443. ++raised;
  1444. };
  1445. using (target.Update())
  1446. {
  1447. target.Deselect(1);
  1448. target.Select(4);
  1449. }
  1450. Assert.Equal(1, raised);
  1451. }
  1452. [Fact]
  1453. public void AutoSelect_Selects_When_Enabled()
  1454. {
  1455. var data = new[] { "foo", "bar", "baz" };
  1456. var target = new SelectionModel { Source = data };
  1457. var raised = 0;
  1458. target.SelectionChanged += (s, e) =>
  1459. {
  1460. Assert.Empty(e.DeselectedIndices);
  1461. Assert.Empty(e.DeselectedItems);
  1462. Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices);
  1463. Assert.Equal(new[] { "foo" }, e.SelectedItems);
  1464. ++raised;
  1465. };
  1466. target.AutoSelect = true;
  1467. Assert.Equal(new IndexPath(0), target.SelectedIndex);
  1468. Assert.Equal(1, raised);
  1469. }
  1470. [Fact]
  1471. public void AutoSelect_Selects_When_Source_Assigned()
  1472. {
  1473. var data = new[] { "foo", "bar", "baz" };
  1474. var target = new SelectionModel { AutoSelect = true };
  1475. var raised = 0;
  1476. target.SelectionChanged += (s, e) =>
  1477. {
  1478. Assert.Empty(e.DeselectedIndices);
  1479. Assert.Empty(e.DeselectedItems);
  1480. Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices);
  1481. Assert.Equal(new[] { "foo" }, e.SelectedItems);
  1482. ++raised;
  1483. };
  1484. target.Source = data;
  1485. Assert.Equal(new IndexPath(0), target.SelectedIndex);
  1486. Assert.Equal(1, raised);
  1487. }
  1488. [Fact]
  1489. public void AutoSelect_Selects_When_New_Source_Assigned_And_Old_Source_Has_Selection()
  1490. {
  1491. var data = new[] { "foo", "bar", "baz" };
  1492. var target = new SelectionModel { AutoSelect = true, Source = data };
  1493. var raised = 0;
  1494. target.SelectionChanged += (s, e) =>
  1495. {
  1496. if (raised == 0)
  1497. {
  1498. Assert.Equal(new[] { new IndexPath(0) }, e.DeselectedIndices);
  1499. Assert.Equal(new[] { "foo" }, e.DeselectedItems);
  1500. Assert.Empty(e.SelectedIndices);
  1501. Assert.Empty(e.SelectedItems);
  1502. }
  1503. else
  1504. {
  1505. Assert.Empty(e.DeselectedIndices);
  1506. Assert.Empty(e.DeselectedItems);
  1507. Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices);
  1508. Assert.Equal(new[] { "newfoo" }, e.SelectedItems);
  1509. }
  1510. ++raised;
  1511. };
  1512. target.Source = new[] { "newfoo" };
  1513. Assert.Equal(new IndexPath(0), target.SelectedIndex);
  1514. Assert.Equal(2, raised);
  1515. }
  1516. [Fact]
  1517. public void AutoSelect_Selects_When_First_Item_Added()
  1518. {
  1519. var data = new ObservableCollection<string>();
  1520. var target = new SelectionModel { AutoSelect = true , Source = data };
  1521. var raised = 0;
  1522. target.SelectionChanged += (s, e) =>
  1523. {
  1524. Assert.Empty(e.DeselectedIndices);
  1525. Assert.Empty(e.DeselectedItems);
  1526. Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices);
  1527. Assert.Equal(new[] { "foo" }, e.SelectedItems);
  1528. ++raised;
  1529. };
  1530. data.Add("foo");
  1531. Assert.Equal(new IndexPath(0), target.SelectedIndex);
  1532. Assert.Equal(1, raised);
  1533. }
  1534. [Fact]
  1535. public void AutoSelect_Selects_When_Selected_Item_Removed()
  1536. {
  1537. var data = new ObservableCollection<string> { "foo", "bar", "baz" };
  1538. var target = new SelectionModel { AutoSelect = true, Source = data };
  1539. var raised = 0;
  1540. target.SelectedIndex = new IndexPath(2);
  1541. target.SelectionChanged += (s, e) =>
  1542. {
  1543. if (raised == 0)
  1544. {
  1545. Assert.Empty(e.DeselectedIndices);
  1546. Assert.Equal(new[] { "baz" }, e.DeselectedItems);
  1547. Assert.Empty(e.SelectedIndices);
  1548. Assert.Empty(e.SelectedItems);
  1549. }
  1550. else
  1551. {
  1552. Assert.Empty(e.DeselectedIndices);
  1553. Assert.Empty(e.DeselectedItems);
  1554. Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices);
  1555. Assert.Equal(new[] { "foo" }, e.SelectedItems);
  1556. }
  1557. ++raised;
  1558. };
  1559. data.RemoveAt(2);
  1560. Assert.Equal(new IndexPath(0), target.SelectedIndex);
  1561. Assert.Equal(2, raised);
  1562. }
  1563. [Fact]
  1564. public void AutoSelect_Selects_On_Deselection()
  1565. {
  1566. var data = new[] { "foo", "bar", "baz" };
  1567. var target = new SelectionModel { AutoSelect = true, Source = data };
  1568. var raised = 0;
  1569. target.SelectedIndex = new IndexPath(2);
  1570. target.SelectionChanged += (s, e) =>
  1571. {
  1572. Assert.Equal(new[] { new IndexPath(2) }, e.DeselectedIndices);
  1573. Assert.Equal(new[] { "baz" }, e.DeselectedItems);
  1574. Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices);
  1575. Assert.Equal(new[] { "foo" }, e.SelectedItems);
  1576. ++raised;
  1577. };
  1578. target.Deselect(2);
  1579. Assert.Equal(new IndexPath(0), target.SelectedIndex);
  1580. Assert.Equal(1, raised);
  1581. }
  1582. [Fact]
  1583. public void AutoSelect_Selects_On_ClearSelection()
  1584. {
  1585. var data = new[] { "foo", "bar", "baz" };
  1586. var target = new SelectionModel { AutoSelect = true, Source = data };
  1587. var raised = 0;
  1588. target.SelectedIndex = new IndexPath(2);
  1589. target.SelectionChanged += (s, e) =>
  1590. {
  1591. Assert.Equal(new[] { new IndexPath(2) }, e.DeselectedIndices);
  1592. Assert.Equal(new[] { "baz" }, e.DeselectedItems);
  1593. Assert.Equal(new[] { new IndexPath(0) }, e.SelectedIndices);
  1594. Assert.Equal(new[] { "foo" }, e.SelectedItems);
  1595. ++raised;
  1596. };
  1597. target.ClearSelection();
  1598. Assert.Equal(new IndexPath(0), target.SelectedIndex);
  1599. Assert.Equal(1, raised);
  1600. }
  1601. [Fact]
  1602. public void AutoSelect_Overrides_Deselecting_First_Item()
  1603. {
  1604. var data = new[] { "foo", "bar", "baz" };
  1605. var target = new SelectionModel { AutoSelect = true, Source = data };
  1606. var raised = 0;
  1607. target.Select(0);
  1608. target.SelectionChanged += (s, e) =>
  1609. {
  1610. ++raised;
  1611. };
  1612. target.Deselect(0);
  1613. Assert.Equal(new IndexPath(0), target.SelectedIndex);
  1614. Assert.Equal(0, raised);
  1615. }
  1616. [Fact]
  1617. public void Can_Replace_Children_Collection()
  1618. {
  1619. var root = new Node("Root");
  1620. var target = new SelectionModel { Source = new[] { root } };
  1621. target.ChildrenRequested += (s, e) => e.Children = ((Node)e.Source).WhenAnyValue(x => x.Children);
  1622. target.Select(0, 9);
  1623. Assert.Equal("Child 9", ((Node)target.SelectedItem).Header);
  1624. root.ReplaceChildren();
  1625. Assert.Null(target.SelectedItem);
  1626. }
  1627. [Fact]
  1628. public void Child_Resolver_Is_Unsubscribed_When_Source_Changed()
  1629. {
  1630. var root = new Node("Root");
  1631. var target = new SelectionModel { Source = new[] { root } };
  1632. target.ChildrenRequested += (s, e) => e.Children = ((Node)e.Source).WhenAnyValue(x => x.Children);
  1633. target.Select(0, 9);
  1634. Assert.Equal(1, root.PropertyChangedSubscriptions);
  1635. target.Source = null;
  1636. Assert.Equal(0, root.PropertyChangedSubscriptions);
  1637. }
  1638. [Fact]
  1639. public void Child_Resolver_Is_Unsubscribed_When_Parent_Removed()
  1640. {
  1641. var root = new Node("Root");
  1642. var target = new SelectionModel { Source = new[] { root } };
  1643. var node = root.Children[1];
  1644. var path = new IndexPath(new[] { 0, 1, 1 });
  1645. target.ChildrenRequested += (s, e) => e.Children = ((Node)e.Source).WhenAnyValue(x => x.Children);
  1646. target.SelectAt(path);
  1647. Assert.Equal(1, node.PropertyChangedSubscriptions);
  1648. root.ReplaceChildren();
  1649. Assert.Equal(0, node.PropertyChangedSubscriptions);
  1650. }
  1651. private class Node : INotifyPropertyChanged
  1652. {
  1653. private ObservableCollection<Node> _children;
  1654. private PropertyChangedEventHandler _propertyChanged;
  1655. public Node(string header)
  1656. {
  1657. Header = header;
  1658. }
  1659. public string Header { get; }
  1660. public ObservableCollection<Node> Children
  1661. {
  1662. get => _children ??= CreateChildren(10);
  1663. private set
  1664. {
  1665. _children = value;
  1666. _propertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Children)));
  1667. }
  1668. }
  1669. public event PropertyChangedEventHandler PropertyChanged
  1670. {
  1671. add
  1672. {
  1673. _propertyChanged += value;
  1674. ++PropertyChangedSubscriptions;
  1675. }
  1676. remove
  1677. {
  1678. _propertyChanged -= value;
  1679. --PropertyChangedSubscriptions;
  1680. }
  1681. }
  1682. public int PropertyChangedSubscriptions { get; private set; }
  1683. public void ReplaceChildren()
  1684. {
  1685. Children = CreateChildren(5);
  1686. }
  1687. private ObservableCollection<Node> CreateChildren(int count)
  1688. {
  1689. return new ObservableCollection<Node>(
  1690. Enumerable.Range(0, count).Select(x => new Node("Child " + x)));
  1691. }
  1692. }
  1693. private int GetSubscriberCount(AvaloniaList<object> list)
  1694. {
  1695. return ((INotifyCollectionChangedDebug)list).GetCollectionChangedSubscribers()?.Length ?? 0;
  1696. }
  1697. private void VerifyCollectionChangedHandlers(int expected, AvaloniaList<object> list)
  1698. {
  1699. var count = GetSubscriberCount(list);
  1700. Assert.Equal(expected, count);
  1701. foreach (var i in list)
  1702. {
  1703. if (i is AvaloniaList<object> l)
  1704. {
  1705. VerifyCollectionChangedHandlers(expected, l);
  1706. }
  1707. }
  1708. }
  1709. private void Select(SelectionModel manager, int index, bool select)
  1710. {
  1711. _output.WriteLine((select ? "Selecting " : "DeSelecting ") + index);
  1712. if (select)
  1713. {
  1714. manager.Select(index);
  1715. }
  1716. else
  1717. {
  1718. manager.Deselect(index);
  1719. }
  1720. }
  1721. private void Select(SelectionModel manager, int groupIndex, int itemIndex, bool select)
  1722. {
  1723. _output.WriteLine((select ? "Selecting " : "DeSelecting ") + groupIndex + "." + itemIndex);
  1724. if (select)
  1725. {
  1726. manager.Select(groupIndex, itemIndex);
  1727. }
  1728. else
  1729. {
  1730. manager.Deselect(groupIndex, itemIndex);
  1731. }
  1732. }
  1733. private void Select(SelectionModel manager, IndexPath index, bool select)
  1734. {
  1735. _output.WriteLine((select ? "Selecting " : "DeSelecting ") + index);
  1736. if (select)
  1737. {
  1738. manager.SelectAt(index);
  1739. }
  1740. else
  1741. {
  1742. manager.DeselectAt(index);
  1743. }
  1744. }
  1745. private void SelectRangeFromAnchor(SelectionModel manager, int index, bool select)
  1746. {
  1747. _output.WriteLine("SelectRangeFromAnchor " + index + " select: " + select.ToString());
  1748. if (select)
  1749. {
  1750. manager.SelectRangeFromAnchor(index);
  1751. }
  1752. else
  1753. {
  1754. manager.DeselectRangeFromAnchor(index);
  1755. }
  1756. }
  1757. private void SelectRangeFromAnchor(SelectionModel manager, int groupIndex, int itemIndex, bool select)
  1758. {
  1759. _output.WriteLine("SelectRangeFromAnchor " + groupIndex + "." + itemIndex + " select:" + select.ToString());
  1760. if (select)
  1761. {
  1762. manager.SelectRangeFromAnchor(groupIndex, itemIndex);
  1763. }
  1764. else
  1765. {
  1766. manager.DeselectRangeFromAnchor(groupIndex, itemIndex);
  1767. }
  1768. }
  1769. private void SelectRangeFromAnchor(SelectionModel manager, IndexPath index, bool select)
  1770. {
  1771. _output.WriteLine("SelectRangeFromAnchor " + index + " select: " + select.ToString());
  1772. if (select)
  1773. {
  1774. manager.SelectRangeFromAnchorTo(index);
  1775. }
  1776. else
  1777. {
  1778. manager.DeselectRangeFromAnchorTo(index);
  1779. }
  1780. }
  1781. private void ClearSelection(SelectionModel manager)
  1782. {
  1783. _output.WriteLine("ClearSelection");
  1784. manager.ClearSelection();
  1785. }
  1786. private void SetAnchorIndex(SelectionModel manager, int index)
  1787. {
  1788. _output.WriteLine("SetAnchorIndex " + index);
  1789. manager.SetAnchorIndex(index);
  1790. }
  1791. private void SetAnchorIndex(SelectionModel manager, int groupIndex, int itemIndex)
  1792. {
  1793. _output.WriteLine("SetAnchor " + groupIndex + "." + itemIndex);
  1794. manager.SetAnchorIndex(groupIndex, itemIndex);
  1795. }
  1796. private void SetAnchorIndex(SelectionModel manager, IndexPath index)
  1797. {
  1798. _output.WriteLine("SetAnchor " + index);
  1799. manager.AnchorIndex = index;
  1800. }
  1801. private void ValidateSelection(
  1802. SelectionModel selectionModel,
  1803. List<IndexPath> expectedSelected,
  1804. List<IndexPath> expectedPartialSelected = null,
  1805. int selectedInnerNodes = 0)
  1806. {
  1807. _output.WriteLine("Validating Selection...");
  1808. _output.WriteLine("Selection contains indices:");
  1809. foreach (var index in selectionModel.SelectedIndices)
  1810. {
  1811. _output.WriteLine(" " + index.ToString());
  1812. }
  1813. _output.WriteLine("Selection contains items:");
  1814. foreach (var item in selectionModel.SelectedItems)
  1815. {
  1816. _output.WriteLine(" " + item.ToString());
  1817. }
  1818. if (selectionModel.Source != null)
  1819. {
  1820. List<IndexPath> allIndices = GetIndexPathsInSource(selectionModel.Source);
  1821. foreach (var index in allIndices)
  1822. {
  1823. bool? isSelected = selectionModel.IsSelectedWithPartialAt(index);
  1824. if (Contains(expectedSelected, index) && !Contains(expectedPartialSelected, index))
  1825. {
  1826. Assert.True(isSelected.Value, index + " is Selected");
  1827. }
  1828. else if (expectedPartialSelected != null && Contains(expectedPartialSelected, index))
  1829. {
  1830. Assert.True(isSelected == null, index + " is partially Selected");
  1831. }
  1832. else
  1833. {
  1834. if (isSelected == null)
  1835. {
  1836. _output.WriteLine("*************" + index + " is null");
  1837. Assert.True(false, "Expected false but got null");;
  1838. }
  1839. else
  1840. {
  1841. Assert.False(isSelected.Value, index + " is not Selected");
  1842. }
  1843. }
  1844. }
  1845. }
  1846. else
  1847. {
  1848. foreach (var index in expectedSelected)
  1849. {
  1850. Assert.True(selectionModel.IsSelectedWithPartialAt(index), index + " is Selected");
  1851. }
  1852. }
  1853. if (expectedSelected.Count > 0)
  1854. {
  1855. _output.WriteLine("SelectedIndex is " + selectionModel.SelectedIndex);
  1856. Assert.Equal(expectedSelected[0], selectionModel.SelectedIndex);
  1857. if (selectionModel.Source != null)
  1858. {
  1859. Assert.Equal(selectionModel.SelectedItem, GetData(selectionModel, expectedSelected[0]));
  1860. }
  1861. int itemsCount = selectionModel.SelectedItems.Count();
  1862. Assert.Equal(selectionModel.Source != null ? expectedSelected.Count - selectedInnerNodes : 0, itemsCount);
  1863. int indicesCount = selectionModel.SelectedIndices.Count();
  1864. Assert.Equal(expectedSelected.Count - selectedInnerNodes, indicesCount);
  1865. }
  1866. _output.WriteLine("Validating Selection... done");
  1867. }
  1868. private object GetData(SelectionModel selectionModel, IndexPath indexPath)
  1869. {
  1870. var data = selectionModel.Source;
  1871. for (int i = 0; i < indexPath.GetSize(); i++)
  1872. {
  1873. var listData = data as IList;
  1874. data = listData[indexPath.GetAt(i)];
  1875. }
  1876. return data;
  1877. }
  1878. private bool AreEqual(IndexPath a, IndexPath b)
  1879. {
  1880. if (a.GetSize() != b.GetSize())
  1881. {
  1882. return false;
  1883. }
  1884. for (int i = 0; i < a.GetSize(); i++)
  1885. {
  1886. if (a.GetAt(i) != b.GetAt(i))
  1887. {
  1888. return false;
  1889. }
  1890. }
  1891. return true;
  1892. }
  1893. private List<IndexPath> GetIndexPathsInSource(object source)
  1894. {
  1895. List<IndexPath> paths = new List<IndexPath>();
  1896. Traverse(source, (TreeWalkNodeInfo node) =>
  1897. {
  1898. if (!paths.Contains(node.Path))
  1899. {
  1900. paths.Add(node.Path);
  1901. }
  1902. });
  1903. _output.WriteLine("All Paths in source..");
  1904. foreach (var path in paths)
  1905. {
  1906. _output.WriteLine(path.ToString());
  1907. }
  1908. _output.WriteLine("done.");
  1909. return paths;
  1910. }
  1911. private static void Traverse(object root, Action<TreeWalkNodeInfo> nodeAction)
  1912. {
  1913. var pendingNodes = new Stack<TreeWalkNodeInfo>();
  1914. IndexPath current = Path(null);
  1915. pendingNodes.Push(new TreeWalkNodeInfo() { Current = root, Path = current });
  1916. while (pendingNodes.Count > 0)
  1917. {
  1918. var currentNode = pendingNodes.Pop();
  1919. var currentObject = currentNode.Current as IList;
  1920. if (currentObject != null)
  1921. {
  1922. for (int i = currentObject.Count - 1; i >= 0; i--)
  1923. {
  1924. var child = currentObject[i];
  1925. List<int> path = new List<int>();
  1926. for (int idx = 0; idx < currentNode.Path.GetSize(); idx++)
  1927. {
  1928. path.Add(currentNode.Path.GetAt(idx));
  1929. }
  1930. path.Add(i);
  1931. var childPath = IndexPath.CreateFromIndices(path);
  1932. if (child != null)
  1933. {
  1934. pendingNodes.Push(new TreeWalkNodeInfo() { Current = child, Path = childPath });
  1935. }
  1936. }
  1937. }
  1938. nodeAction(currentNode);
  1939. }
  1940. }
  1941. private bool Contains(List<IndexPath> list, IndexPath index)
  1942. {
  1943. bool contains = false;
  1944. foreach (var item in list)
  1945. {
  1946. if (item.CompareTo(index) == 0)
  1947. {
  1948. contains = true;
  1949. break;
  1950. }
  1951. }
  1952. return contains;
  1953. }
  1954. public static AvaloniaList<object> CreateNestedData(int levels = 3, int groupsAtLevel = 5, int countAtLeaf = 10)
  1955. {
  1956. var nextData = 0;
  1957. return CreateNestedData(levels, groupsAtLevel, countAtLeaf, ref nextData);
  1958. }
  1959. public static AvaloniaList<object> CreateNestedData(
  1960. int levels,
  1961. int groupsAtLevel,
  1962. int countAtLeaf,
  1963. ref int nextData)
  1964. {
  1965. var data = new AvaloniaList<object>();
  1966. if (levels != 0)
  1967. {
  1968. for (int i = 0; i < groupsAtLevel; i++)
  1969. {
  1970. data.Add(CreateNestedData(levels - 1, groupsAtLevel, countAtLeaf, ref nextData));
  1971. }
  1972. }
  1973. else
  1974. {
  1975. for (int i = 0; i < countAtLeaf; i++)
  1976. {
  1977. data.Add(nextData++);
  1978. }
  1979. }
  1980. return data;
  1981. }
  1982. static IndexPath Path(params int[] path)
  1983. {
  1984. return IndexPath.CreateFromIndices(path);
  1985. }
  1986. private static int _nextData = 0;
  1987. private struct TreeWalkNodeInfo
  1988. {
  1989. public object Current { get; set; }
  1990. public IndexPath Path { get; set; }
  1991. }
  1992. private class ResettingList<T> : List<object>, INotifyCollectionChanged
  1993. {
  1994. public event NotifyCollectionChangedEventHandler CollectionChanged;
  1995. public new void RemoveAt(int index)
  1996. {
  1997. var item = this[index];
  1998. base.RemoveAt(index);
  1999. CollectionChanged?.Invoke(
  2000. this,
  2001. new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new[] { item }, index));
  2002. }
  2003. public void Reset(IEnumerable<object> items = null)
  2004. {
  2005. if (items != null)
  2006. {
  2007. Clear();
  2008. AddRange(items);
  2009. }
  2010. CollectionChanged?.Invoke(
  2011. this,
  2012. new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  2013. }
  2014. }
  2015. }
  2016. class CustomSelectionModel : SelectionModel
  2017. {
  2018. public int IntProperty
  2019. {
  2020. get { return _intProperty; }
  2021. set
  2022. {
  2023. _intProperty = value;
  2024. OnPropertyChanged("IntProperty");
  2025. }
  2026. }
  2027. private int _intProperty;
  2028. }
  2029. }