SelectionModelTests.cs 77 KB

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