SelectionModelTests_Single.cs 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. using System;
  2. using System.Collections.Specialized;
  3. using Avalonia.Collections;
  4. using Avalonia.Controls.Selection;
  5. using Avalonia.Controls.Utils;
  6. using Xunit;
  7. using CollectionChangedEventManager = Avalonia.Controls.Utils.CollectionChangedEventManager;
  8. #nullable enable
  9. namespace Avalonia.Controls.UnitTests.Selection
  10. {
  11. public class SelectionModelTests_Single
  12. {
  13. public class Source
  14. {
  15. [Fact]
  16. public void Can_Select_Index_Before_Source_Assigned()
  17. {
  18. var target = CreateTarget(false);
  19. var raised = 0;
  20. target.SelectionChanged += (s, e) =>
  21. {
  22. Assert.Empty(e.DeselectedIndexes);
  23. Assert.Empty(e.DeselectedItems);
  24. Assert.Equal(new[] { 5 }, e.SelectedIndexes);
  25. Assert.Equal(new string?[] { null }, e.SelectedItems);
  26. ++raised;
  27. };
  28. target.SelectedIndex = 5;
  29. Assert.Equal(5, target.SelectedIndex);
  30. Assert.Equal(new[] { 5 }, target.SelectedIndexes);
  31. Assert.Null(target.SelectedItem);
  32. Assert.Equal(new string?[] { null }, target.SelectedItems);
  33. Assert.Equal(1, raised);
  34. }
  35. [Fact]
  36. public void Can_Select_Item_Before_Source_Assigned()
  37. {
  38. var target = CreateTarget(false);
  39. var raised = 0;
  40. target.SelectionChanged += (s, e) => ++raised;
  41. target.SelectedItem = "bar";
  42. Assert.Equal(-1, target.SelectedIndex);
  43. Assert.Empty(target.SelectedIndexes);
  44. Assert.Equal("bar", target.SelectedItem);
  45. Assert.Equal(new string?[] { "bar" }, target.SelectedItems);
  46. Assert.Equal(0, raised);
  47. }
  48. [Fact]
  49. public void Initializing_Source_Retains_Valid_Index_Selection()
  50. {
  51. var target = CreateTarget(false);
  52. var raised = 0;
  53. target.SelectedIndex = 1;
  54. target.SelectionChanged += (s, e) => ++raised;
  55. target.Source = new[] { "foo", "bar", "baz" };
  56. Assert.Equal(1, target.SelectedIndex);
  57. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  58. Assert.Equal("bar", target.SelectedItem);
  59. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  60. Assert.Equal(0, raised);
  61. }
  62. [Fact]
  63. public void Initializing_Source_Removes_Invalid_Index_Selection()
  64. {
  65. var target = CreateTarget(false);
  66. var raised = 0;
  67. target.SelectedIndex = 5;
  68. target.SelectionChanged += (s, e) =>
  69. {
  70. Assert.Equal(new[] { 5 }, e.DeselectedIndexes);
  71. Assert.Equal(new string?[] { null }, e.DeselectedItems);
  72. Assert.Empty(e.SelectedIndexes);
  73. Assert.Empty(e.SelectedItems);
  74. ++raised;
  75. };
  76. target.Source = new[] { "foo", "bar", "baz" };
  77. Assert.Equal(-1, target.SelectedIndex);
  78. Assert.Empty(target.SelectedIndexes);
  79. Assert.Null(target.SelectedItem);
  80. Assert.Empty(target.SelectedItems);
  81. Assert.Equal(1, raised);
  82. }
  83. [Fact]
  84. public void Initializing_Source_Retains_Valid_Item_Selection()
  85. {
  86. var target = CreateTarget(false);
  87. var raised = 0;
  88. target.SelectedItem = "bar";
  89. target.SelectionChanged += (s, e) =>
  90. {
  91. Assert.Empty(e.DeselectedIndexes);
  92. Assert.Empty(e.DeselectedItems);
  93. Assert.Equal(new[] { 1 }, e.SelectedIndexes);
  94. Assert.Equal(new string[] { "bar" }, e.SelectedItems);
  95. ++raised;
  96. };
  97. target.Source = new[] { "foo", "bar", "baz" };
  98. Assert.Equal(1, target.SelectedIndex);
  99. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  100. Assert.Equal("bar", target.SelectedItem);
  101. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  102. Assert.Equal(1, raised);
  103. }
  104. [Fact]
  105. public void Initializing_Source_Removes_Invalid_Item_Selection()
  106. {
  107. var target = CreateTarget(false);
  108. var raised = 0;
  109. target.SelectedItem = "qux";
  110. target.SelectionChanged += (s, e) => ++raised;
  111. target.Source = new[] { "foo", "bar", "baz" };
  112. Assert.Equal(-1, target.SelectedIndex);
  113. Assert.Empty(target.SelectedIndexes);
  114. Assert.Null(target.SelectedItem);
  115. Assert.Empty(target.SelectedItems);
  116. Assert.Equal(0, raised);
  117. }
  118. [Fact]
  119. public void Initializing_Source_Respects_SourceIndex_SourceItem_Order()
  120. {
  121. var target = CreateTarget(false);
  122. target.SelectedIndex = 0;
  123. target.SelectedItem = "bar";
  124. target.Source = new[] { "foo", "bar", "baz" };
  125. Assert.Equal(1, target.SelectedIndex);
  126. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  127. Assert.Equal("bar", target.SelectedItem);
  128. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  129. }
  130. [Fact]
  131. public void Initializing_Source_Respects_SourceItem_SourceIndex_Order()
  132. {
  133. var target = CreateTarget(false);
  134. target.SelectedItem = "foo";
  135. target.SelectedIndex = 1;
  136. target.Source = new[] { "foo", "bar", "baz" };
  137. Assert.Equal(1, target.SelectedIndex);
  138. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  139. Assert.Equal("bar", target.SelectedItem);
  140. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  141. }
  142. [Fact]
  143. public void Initializing_Source_Raises_SelectedItems_PropertyChanged()
  144. {
  145. var target = CreateTarget(false);
  146. var selectedItemRaised = 0;
  147. var selectedItemsRaised = 0;
  148. target.Select(1);
  149. target.PropertyChanged += (s, e) =>
  150. {
  151. if (e.PropertyName == nameof(target.SelectedItem))
  152. {
  153. ++selectedItemRaised;
  154. }
  155. else if (e.PropertyName == nameof(target.SelectedItems))
  156. {
  157. ++selectedItemsRaised;
  158. }
  159. };
  160. target.Source = new[] { "foo", "bar", "baz" };
  161. Assert.Equal(1, selectedItemRaised);
  162. Assert.Equal(1, selectedItemsRaised);
  163. }
  164. [Fact]
  165. public void Changing_Source_To_Null_Doesnt_Clear_Selection()
  166. {
  167. var target = CreateTarget();
  168. var raised = 0;
  169. target.SelectedIndex = 2;
  170. target.SelectionChanged += (s, e) => ++raised;
  171. target.Source = null;
  172. Assert.Equal(2, target.SelectedIndex);
  173. Assert.Equal(new[] { 2 }, target.SelectedIndexes);
  174. Assert.Null(target.SelectedItem);
  175. Assert.Equal(new string?[] { null }, target.SelectedItems);
  176. Assert.Equal(0, raised);
  177. }
  178. [Fact]
  179. public void Changing_Source_To_NonNull_First_Clears_Old_Selection()
  180. {
  181. var target = CreateTarget();
  182. var raised = 0;
  183. target.SelectedIndex = 2;
  184. target.SelectionChanged += (s, e) =>
  185. {
  186. Assert.Equal(new[] { 2 }, e.DeselectedIndexes);
  187. Assert.Equal(new string?[] { "baz" }, e.DeselectedItems);
  188. Assert.Empty(e.SelectedIndexes);
  189. Assert.Empty(e.SelectedItems);
  190. ++raised;
  191. };
  192. target.Source = new[] { "qux", "quux", "corge" };
  193. Assert.Equal(-1, target.SelectedIndex);
  194. Assert.Empty(target.SelectedIndexes);
  195. Assert.Null(target.SelectedItem);
  196. Assert.Empty(target.SelectedItems);
  197. Assert.Equal(1, raised);
  198. }
  199. [Fact]
  200. public void Changing_Source_To_Null_Raises_SelectedItems_PropertyChanged()
  201. {
  202. var target = CreateTarget();
  203. var selectedItemRaised = 0;
  204. var selectedItemsRaised = 0;
  205. target.Select(1);
  206. target.PropertyChanged += (s, e) =>
  207. {
  208. if (e.PropertyName == nameof(target.SelectedItem))
  209. {
  210. ++selectedItemRaised;
  211. }
  212. else if (e.PropertyName == nameof(target.SelectedItems))
  213. {
  214. ++selectedItemsRaised;
  215. }
  216. };
  217. target.Source = null;
  218. Assert.Equal(1, selectedItemRaised);
  219. Assert.Equal(1, selectedItemsRaised);
  220. }
  221. [Fact]
  222. public void Raises_PropertyChanged()
  223. {
  224. var target = CreateTarget();
  225. var raised = 0;
  226. target.PropertyChanged += (s, e) =>
  227. {
  228. if (e.PropertyName == nameof(target.Source))
  229. {
  230. ++raised;
  231. }
  232. };
  233. target.Source = new[] { "qux", "quux", "corge" };
  234. Assert.Equal(1, raised);
  235. }
  236. [Fact]
  237. public void Can_Assign_ValueType_Collection_To_SelectionModel_Of_Object()
  238. {
  239. var target = (ISelectionModel)new SelectionModel<object>();
  240. target.Source = new[] { 1, 2, 3 };
  241. }
  242. }
  243. public class SelectedIndex
  244. {
  245. [Fact]
  246. public void SelectedIndex_Larger_Than_Source_Clears_Selection()
  247. {
  248. var target = CreateTarget();
  249. var raised = 0;
  250. target.SelectedIndex = 1;
  251. target.SelectionChanged += (s, e) =>
  252. {
  253. Assert.Equal(new[] { 1 }, e.DeselectedIndexes);
  254. Assert.Equal(new[] { "bar" }, e.DeselectedItems);
  255. Assert.Empty(e.SelectedIndexes);
  256. Assert.Empty(e.SelectedItems);
  257. ++raised;
  258. };
  259. target.SelectedIndex = 5;
  260. Assert.Equal(-1, target.SelectedIndex);
  261. Assert.Empty(target.SelectedIndexes);
  262. Assert.Null(target.SelectedItem);
  263. Assert.Empty(target.SelectedItems);
  264. Assert.Equal(1, raised);
  265. }
  266. [Fact]
  267. public void Negative_SelectedIndex_Is_Coerced_To_Minus_1()
  268. {
  269. var target = CreateTarget();
  270. var raised = 0;
  271. target.SelectionChanged += (s, e) => ++raised;
  272. target.SelectedIndex = -5;
  273. Assert.Equal(-1, target.SelectedIndex);
  274. Assert.Empty(target.SelectedIndexes);
  275. Assert.Null(target.SelectedItem);
  276. Assert.Empty(target.SelectedItems);
  277. Assert.Equal(0, raised);
  278. }
  279. [Fact]
  280. public void Setting_SelectedIndex_Clears_Old_Selection()
  281. {
  282. var target = CreateTarget();
  283. var raised = 0;
  284. target.SelectedIndex = 0;
  285. target.SelectionChanged += (s, e) =>
  286. {
  287. Assert.Equal(new[] { 0 }, e.DeselectedIndexes);
  288. Assert.Equal(new[] { "foo" }, e.DeselectedItems);
  289. Assert.Equal(new[] { 1 }, e.SelectedIndexes);
  290. Assert.Equal(new[] { "bar" }, e.SelectedItems);
  291. ++raised;
  292. };
  293. target.SelectedIndex = 1;
  294. Assert.Equal(1, target.SelectedIndex);
  295. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  296. Assert.Equal("bar", target.SelectedItem);
  297. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  298. Assert.Equal(1, raised);
  299. }
  300. [Fact]
  301. public void Setting_SelectedIndex_During_CollectionChanged_Results_In_Correct_Selection()
  302. {
  303. // Issue #4496
  304. var data = new AvaloniaList<string>();
  305. var target = CreateTarget();
  306. var binding = new MockBinding(target, data);
  307. target.Source = data;
  308. data.Add("foo");
  309. Assert.Equal(0, target.SelectedIndex);
  310. }
  311. [Fact]
  312. public void PropertyChanged_Is_Raised()
  313. {
  314. var target = CreateTarget();
  315. var raised = 0;
  316. target.PropertyChanged += (s, e) =>
  317. {
  318. if (e.PropertyName == nameof(target.SelectedIndex))
  319. {
  320. ++raised;
  321. }
  322. };
  323. target.SelectedIndex = 1;
  324. Assert.Equal(1, raised);
  325. }
  326. private class MockBinding : ICollectionChangedListener
  327. {
  328. private readonly SelectionModel<string?> _target;
  329. public MockBinding(SelectionModel<string?> target, AvaloniaList<string> data)
  330. {
  331. _target = target;
  332. CollectionChangedEventManager.Instance.AddListener(data, this);
  333. }
  334. public void Changed(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e)
  335. {
  336. _target.Select(0);
  337. }
  338. public void PostChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e)
  339. {
  340. }
  341. public void PreChanged(INotifyCollectionChanged sender, NotifyCollectionChangedEventArgs e)
  342. {
  343. }
  344. }
  345. }
  346. public class SelectedItem
  347. {
  348. [Fact]
  349. public void Setting_SelectedItem_To_Valid_Item_Updates_Selection()
  350. {
  351. var target = CreateTarget();
  352. var raised = 0;
  353. target.SelectionChanged += (s, e) =>
  354. {
  355. Assert.Empty(e.DeselectedIndexes);
  356. Assert.Empty(e.DeselectedItems);
  357. Assert.Equal(new[] { 1 }, e.SelectedIndexes);
  358. Assert.Equal(new[] { "bar" }, e.SelectedItems);
  359. ++raised;
  360. };
  361. target.SelectedItem = "bar";
  362. Assert.Equal(1, raised);
  363. }
  364. [Fact]
  365. public void PropertyChanged_Is_Raised_When_SelectedIndex_Changes()
  366. {
  367. var target = CreateTarget();
  368. var raised = 0;
  369. target.PropertyChanged += (s, e) =>
  370. {
  371. if (e.PropertyName == nameof(target.SelectedItem))
  372. {
  373. ++raised;
  374. }
  375. };
  376. target.SelectedIndex = 1;
  377. Assert.Equal(1, raised);
  378. }
  379. }
  380. public class SelectedIndexes
  381. {
  382. [Fact]
  383. public void PropertyChanged_Is_Raised_When_SelectedIndex_Changes()
  384. {
  385. var target = CreateTarget();
  386. var raised = 0;
  387. target.PropertyChanged += (s, e) =>
  388. {
  389. if (e.PropertyName == nameof(target.SelectedIndexes))
  390. {
  391. ++raised;
  392. }
  393. };
  394. target.SelectedIndex = 1;
  395. Assert.Equal(1, raised);
  396. }
  397. }
  398. public class SelectedItems
  399. {
  400. [Fact]
  401. public void PropertyChanged_Is_Raised_When_SelectedIndex_Changes()
  402. {
  403. var target = CreateTarget();
  404. var raised = 0;
  405. target.PropertyChanged += (s, e) =>
  406. {
  407. if (e.PropertyName == nameof(target.SelectedItems))
  408. {
  409. ++raised;
  410. }
  411. };
  412. target.SelectedIndex = 1;
  413. Assert.Equal(1, raised);
  414. }
  415. }
  416. public class Select
  417. {
  418. [Fact]
  419. public void Select_Sets_SelectedIndex()
  420. {
  421. var target = CreateTarget();
  422. var raised = 0;
  423. target.SelectedIndex = 0;
  424. target.PropertyChanged += (s, e) =>
  425. {
  426. if (e.PropertyName == nameof(target.SelectedIndex))
  427. {
  428. ++raised;
  429. }
  430. };
  431. target.Select(1);
  432. Assert.Equal(1, target.SelectedIndex);
  433. Assert.Equal(1, raised);
  434. }
  435. [Fact]
  436. public void Select_Clears_Old_Selection()
  437. {
  438. var target = CreateTarget();
  439. var raised = 0;
  440. target.SelectedIndex = 0;
  441. target.SelectionChanged += (s, e) =>
  442. {
  443. Assert.Equal(new[] { 0 }, e.DeselectedIndexes);
  444. Assert.Equal(new[] { "foo" }, e.DeselectedItems);
  445. Assert.Equal(new[] { 1 }, e.SelectedIndexes);
  446. Assert.Equal(new[] { "bar" }, e.SelectedItems);
  447. ++raised;
  448. };
  449. target.Select(1);
  450. Assert.Equal(1, target.SelectedIndex);
  451. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  452. Assert.Equal("bar", target.SelectedItem);
  453. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  454. Assert.Equal(1, raised);
  455. }
  456. [Fact]
  457. public void Select_With_Invalid_Index_Does_Nothing()
  458. {
  459. var target = CreateTarget();
  460. var raised = 0;
  461. target.SelectedIndex = 0;
  462. target.PropertyChanged += (s, e) => ++raised;
  463. target.SelectionChanged += (s, e) => ++raised;
  464. target.Select(5);
  465. Assert.Equal(0, target.SelectedIndex);
  466. Assert.Equal(new[] { 0 }, target.SelectedIndexes);
  467. Assert.Equal("foo", target.SelectedItem);
  468. Assert.Equal(new[] { "foo" }, target.SelectedItems);
  469. Assert.Equal(0, raised);
  470. }
  471. [Fact]
  472. public void Selecting_Already_Selected_Item_Doesnt_Raise_SelectionChanged()
  473. {
  474. var target = CreateTarget();
  475. var raised = 0;
  476. target.Select(2);
  477. target.SelectionChanged += (s, e) => ++raised;
  478. target.Select(2);
  479. Assert.Equal(0, raised);
  480. }
  481. }
  482. public class SelectRange
  483. {
  484. [Fact]
  485. public void SelectRange_Throws()
  486. {
  487. var target = CreateTarget();
  488. Assert.Throws<InvalidOperationException>(() => target.SelectRange(0, 10));
  489. }
  490. }
  491. public class Deselect
  492. {
  493. [Fact]
  494. public void Deselect_Clears_Current_Selection()
  495. {
  496. var target = CreateTarget();
  497. var raised = 0;
  498. target.SelectedIndex = 0;
  499. target.SelectionChanged += (s, e) =>
  500. {
  501. Assert.Equal(new[] { 0 }, e.DeselectedIndexes);
  502. Assert.Equal(new[] { "foo" }, e.DeselectedItems);
  503. Assert.Empty(e.SelectedIndexes);
  504. Assert.Empty(e.SelectedItems);
  505. ++raised;
  506. };
  507. target.Deselect(0);
  508. Assert.Equal(-1, target.SelectedIndex);
  509. Assert.Empty(target.SelectedIndexes);
  510. Assert.Null(target.SelectedItem);
  511. Assert.Empty(target.SelectedItems);
  512. Assert.Equal(1, raised);
  513. }
  514. [Fact]
  515. public void Deselect_Does_Nothing_For_Nonselected_Item()
  516. {
  517. var target = CreateTarget();
  518. var raised = 0;
  519. target.SelectedIndex = 1;
  520. target.SelectionChanged += (s, e) => ++raised;
  521. target.Deselect(0);
  522. Assert.Equal(1, target.SelectedIndex);
  523. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  524. Assert.Equal("bar", target.SelectedItem);
  525. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  526. Assert.Equal(0, raised);
  527. }
  528. }
  529. public class DeselectRange
  530. {
  531. [Fact]
  532. public void DeselectRange_Clears_Current_Selection_For_Intersecting_Range()
  533. {
  534. var target = CreateTarget();
  535. var raised = 0;
  536. target.SelectedIndex = 0;
  537. target.SelectionChanged += (s, e) =>
  538. {
  539. Assert.Equal(new[] { 0 }, e.DeselectedIndexes);
  540. Assert.Equal(new[] { "foo" }, e.DeselectedItems);
  541. Assert.Empty(e.SelectedIndexes);
  542. Assert.Empty(e.SelectedItems);
  543. ++raised;
  544. };
  545. target.DeselectRange(0, 2);
  546. Assert.Equal(-1, target.SelectedIndex);
  547. Assert.Empty(target.SelectedIndexes);
  548. Assert.Null(target.SelectedItem);
  549. Assert.Empty(target.SelectedItems);
  550. Assert.Equal(1, raised);
  551. }
  552. [Fact]
  553. public void DeselectRange_Does_Nothing_For_Nonintersecting_Range()
  554. {
  555. var target = CreateTarget();
  556. var raised = 0;
  557. target.SelectedIndex = 0;
  558. target.SelectionChanged += (s, e) => ++raised;
  559. target.DeselectRange(1, 2);
  560. Assert.Equal(0, target.SelectedIndex);
  561. Assert.Equal(new[] { 0 }, target.SelectedIndexes);
  562. Assert.Equal("foo", target.SelectedItem);
  563. Assert.Equal(new[] { "foo" }, target.SelectedItems);
  564. Assert.Equal(0, raised);
  565. }
  566. }
  567. public class Clear
  568. {
  569. [Fact]
  570. public void Clear_Raises_SelectionChanged()
  571. {
  572. var target = CreateTarget();
  573. var raised = 0;
  574. target.Select(1);
  575. target.SelectionChanged += (s, e) =>
  576. {
  577. Assert.Equal(new[] { 1 }, e.DeselectedIndexes);
  578. Assert.Equal(new[] { "bar" }, e.DeselectedItems);
  579. Assert.Empty(e.SelectedIndexes);
  580. Assert.Empty(e.SelectedItems);
  581. ++raised;
  582. };
  583. target.Clear();
  584. Assert.Equal(1, raised);
  585. }
  586. }
  587. public class AnchorIndex
  588. {
  589. [Fact]
  590. public void Setting_SelectedIndex_Sets_AnchorIndex()
  591. {
  592. var target = CreateTarget();
  593. var raised = 0;
  594. target.PropertyChanged += (s, e) =>
  595. {
  596. if (e.PropertyName == nameof(target.AnchorIndex))
  597. {
  598. ++raised;
  599. }
  600. };
  601. target.SelectedIndex = 1;
  602. Assert.Equal(1, target.AnchorIndex);
  603. Assert.Equal(1, raised);
  604. }
  605. [Fact]
  606. public void Setting_SelectedIndex_To_Minus_1_Doesnt_Clear_AnchorIndex()
  607. {
  608. var target = CreateTarget();
  609. var raised = 0;
  610. target.SelectedIndex = 1;
  611. target.PropertyChanged += (s, e) =>
  612. {
  613. if (e.PropertyName == nameof(target.AnchorIndex))
  614. {
  615. ++raised;
  616. }
  617. };
  618. target.SelectedIndex = -1;
  619. Assert.Equal(1, target.AnchorIndex);
  620. Assert.Equal(0, raised);
  621. }
  622. [Fact]
  623. public void Select_Sets_AnchorIndex()
  624. {
  625. var target = CreateTarget();
  626. var raised = 0;
  627. target.PropertyChanged += (s, e) =>
  628. {
  629. if (e.PropertyName == nameof(target.AnchorIndex))
  630. {
  631. ++raised;
  632. }
  633. };
  634. target.Select(1);
  635. Assert.Equal(1, target.AnchorIndex);
  636. Assert.Equal(1, raised);
  637. }
  638. [Fact]
  639. public void Deselect_Doesnt_Clear_AnchorIndex()
  640. {
  641. var target = CreateTarget();
  642. var raised = 0;
  643. target.Select(1);
  644. target.PropertyChanged += (s, e) =>
  645. {
  646. if (e.PropertyName == nameof(target.AnchorIndex))
  647. {
  648. ++raised;
  649. }
  650. };
  651. target.Deselect(1);
  652. Assert.Equal(1, target.AnchorIndex);
  653. Assert.Equal(0, raised);
  654. }
  655. [Fact]
  656. public void Raises_PropertyChanged()
  657. {
  658. var target = CreateTarget();
  659. var raised = 0;
  660. target.PropertyChanged += (s, e) =>
  661. {
  662. if (e.PropertyName == nameof(target.AnchorIndex))
  663. {
  664. ++raised;
  665. }
  666. };
  667. target.SelectedIndex = 1;
  668. Assert.Equal(1, raised);
  669. }
  670. }
  671. public class SingleSelect
  672. {
  673. [Fact]
  674. public void Converting_To_Multiple_Selection_Preserves_Selection()
  675. {
  676. var target = CreateTarget();
  677. var raised = 0;
  678. target.SelectedIndex = 1;
  679. target.SelectionChanged += (s, e) => ++raised;
  680. target.SingleSelect = false;
  681. Assert.Equal(1, target.SelectedIndex);
  682. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  683. Assert.Equal("bar", target.SelectedItem);
  684. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  685. Assert.Equal(0, raised);
  686. }
  687. [Fact]
  688. public void Raises_PropertyChanged()
  689. {
  690. var target = CreateTarget();
  691. var raised = 0;
  692. target.PropertyChanged += (s, e) =>
  693. {
  694. if (e.PropertyName == nameof(target.SingleSelect))
  695. {
  696. ++raised;
  697. }
  698. };
  699. target.SingleSelect = false;
  700. Assert.Equal(1, raised);
  701. }
  702. }
  703. public class CollectionChanges
  704. {
  705. [Fact]
  706. public void Adding_Item_Before_Selected_Item_Updates_Indexes()
  707. {
  708. var target = CreateTarget();
  709. var data = (AvaloniaList<string>)target.Source!;
  710. var selectionChangedRaised = 0;
  711. var indexesChangedRaised = 0;
  712. var selectedIndexRaised = 0;
  713. target.SelectedIndex = 1;
  714. target.SelectionChanged += (s, e) => ++selectionChangedRaised;
  715. target.PropertyChanged += (s, e) =>
  716. {
  717. if (e.PropertyName == nameof(target.SelectedIndex))
  718. {
  719. ++selectedIndexRaised;
  720. }
  721. };
  722. target.IndexesChanged += (s, e) =>
  723. {
  724. Assert.Equal(0, e.StartIndex);
  725. Assert.Equal(1, e.Delta);
  726. ++indexesChangedRaised;
  727. };
  728. data.Insert(0, "new");
  729. Assert.Equal(2, target.SelectedIndex);
  730. Assert.Equal(new[] { 2 }, target.SelectedIndexes);
  731. Assert.Equal("bar", target.SelectedItem);
  732. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  733. Assert.Equal(2, target.AnchorIndex);
  734. Assert.Equal(1, indexesChangedRaised);
  735. Assert.Equal(1, selectedIndexRaised);
  736. Assert.Equal(0, selectionChangedRaised);
  737. }
  738. [Fact]
  739. public void Adding_Item_After_Selected_Doesnt_Raise_Events()
  740. {
  741. var target = CreateTarget();
  742. var data = (AvaloniaList<string>)target.Source!;
  743. var raised = 0;
  744. target.SelectedIndex = 1;
  745. target.PropertyChanged += (s, e) => ++raised;
  746. target.SelectionChanged += (s, e) => ++raised;
  747. target.IndexesChanged += (s, e) => ++raised;
  748. data.Insert(2, "new");
  749. Assert.Equal(1, target.SelectedIndex);
  750. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  751. Assert.Equal("bar", target.SelectedItem);
  752. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  753. Assert.Equal(1, target.AnchorIndex);
  754. Assert.Equal(0, raised);
  755. }
  756. [Fact]
  757. public void Removing_Selected_Item_Updates_State()
  758. {
  759. var target = CreateTarget();
  760. var data = (AvaloniaList<string>)target.Source!;
  761. var selectionChangedRaised = 0;
  762. var selectedIndexRaised = 0;
  763. target.Source = data;
  764. target.Select(1);
  765. target.PropertyChanged += (s, e) =>
  766. {
  767. if (e.PropertyName == nameof(target.SelectedIndex))
  768. {
  769. ++selectedIndexRaised;
  770. }
  771. };
  772. target.SelectionChanged += (s, e) =>
  773. {
  774. Assert.Empty(e.DeselectedIndexes);
  775. Assert.Equal(new[] { "bar" }, e.DeselectedItems);
  776. Assert.Empty(e.SelectedIndexes);
  777. Assert.Empty(e.SelectedItems);
  778. ++selectionChangedRaised;
  779. };
  780. data.RemoveAt(1);
  781. Assert.Equal(-1, target.SelectedIndex);
  782. Assert.Empty(target.SelectedIndexes);
  783. Assert.Null(target.SelectedItem);
  784. Assert.Empty(target.SelectedItems);
  785. Assert.Equal(-1, target.AnchorIndex);
  786. Assert.Equal(1, selectionChangedRaised);
  787. Assert.Equal(1, selectedIndexRaised);
  788. }
  789. [Fact]
  790. public void Removing_Item_Before_Selected_Item_Updates_Indexes()
  791. {
  792. var target = CreateTarget();
  793. var data = (AvaloniaList<string>)target.Source!;
  794. var selectionChangedRaised = 0;
  795. var indexesChangedraised = 0;
  796. target.SelectedIndex = 1;
  797. target.SelectionChanged += (s, e) => ++selectionChangedRaised;
  798. target.IndexesChanged += (s, e) =>
  799. {
  800. Assert.Equal(0, e.StartIndex);
  801. Assert.Equal(-1, e.Delta);
  802. ++indexesChangedraised;
  803. };
  804. data.RemoveAt(0);
  805. Assert.Equal(0, target.SelectedIndex);
  806. Assert.Equal(new[] { 0 }, target.SelectedIndexes);
  807. Assert.Equal("bar", target.SelectedItem);
  808. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  809. Assert.Equal(0, target.AnchorIndex);
  810. Assert.Equal(1, indexesChangedraised);
  811. Assert.Equal(0, selectionChangedRaised);
  812. }
  813. [Fact]
  814. public void Removing_Item_After_Selected_Doesnt_Raise_Events()
  815. {
  816. var target = CreateTarget();
  817. var data = (AvaloniaList<string>)target.Source!;
  818. var raised = 0;
  819. target.SelectedIndex = 1;
  820. target.PropertyChanged += (s, e) => ++raised;
  821. target.SelectionChanged += (s, e) => ++raised;
  822. target.IndexesChanged += (s, e) => ++raised;
  823. data.RemoveAt(2);
  824. Assert.Equal(1, target.SelectedIndex);
  825. Assert.Equal(new[] { 1 }, target.SelectedIndexes);
  826. Assert.Equal("bar", target.SelectedItem);
  827. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  828. Assert.Equal(1, target.AnchorIndex);
  829. Assert.Equal(0, raised);
  830. }
  831. [Fact]
  832. public void Replacing_Selected_Item_Updates_State()
  833. {
  834. var target = CreateTarget();
  835. var data = (AvaloniaList<string>)target.Source!;
  836. var selectionChangedRaised = 0;
  837. var selectedIndexRaised = 0;
  838. var selectedItemRaised = 0;
  839. target.Source = data;
  840. target.Select(1);
  841. target.PropertyChanged += (s, e) =>
  842. {
  843. if (e.PropertyName == nameof(target.SelectedIndex))
  844. {
  845. ++selectedIndexRaised;
  846. }
  847. if (e.PropertyName == nameof(target.SelectedItem))
  848. {
  849. ++selectedItemRaised;
  850. }
  851. };
  852. target.SelectionChanged += (s, e) =>
  853. {
  854. Assert.Empty(e.DeselectedIndexes);
  855. Assert.Equal(new[] { "bar" }, e.DeselectedItems);
  856. Assert.Empty(e.SelectedIndexes);
  857. Assert.Empty(e.SelectedItems);
  858. ++selectionChangedRaised;
  859. };
  860. data[1] = "new";
  861. Assert.Equal(-1, target.SelectedIndex);
  862. Assert.Empty(target.SelectedIndexes);
  863. Assert.Null(target.SelectedItem);
  864. Assert.Empty(target.SelectedItems);
  865. Assert.Equal(-1, target.AnchorIndex);
  866. Assert.Equal(1, selectionChangedRaised);
  867. Assert.Equal(1, selectedIndexRaised);
  868. Assert.Equal(1, selectedItemRaised);
  869. }
  870. [Fact]
  871. public void Resetting_Source_Updates_State()
  872. {
  873. var target = CreateTarget();
  874. var data = (AvaloniaList<string>)target.Source!;
  875. var selectionChangedRaised = 0;
  876. var selectedIndexRaised = 0;
  877. var resetRaised = 0;
  878. target.Source = data;
  879. target.Select(1);
  880. target.PropertyChanged += (s, e) =>
  881. {
  882. if (e.PropertyName == nameof(target.SelectedIndex))
  883. {
  884. ++selectedIndexRaised;
  885. }
  886. };
  887. target.SelectionChanged += (s, e) => ++selectionChangedRaised;
  888. target.SourceReset += (s, e) => ++resetRaised;
  889. data.Clear();
  890. Assert.Equal(-1, target.SelectedIndex);
  891. Assert.Empty(target.SelectedIndexes);
  892. Assert.Null(target.SelectedItem);
  893. Assert.Empty(target.SelectedItems);
  894. Assert.Equal(-1, target.AnchorIndex);
  895. Assert.Equal(0, selectionChangedRaised);
  896. Assert.Equal(1, resetRaised);
  897. Assert.Equal(1, selectedIndexRaised);
  898. }
  899. [Fact]
  900. public void Handles_Selection_Made_In_CollectionChanged()
  901. {
  902. // Tests the following scenario:
  903. //
  904. // - Items changes from empty to having 1 item
  905. // - ViewModel auto-selects item 0 in CollectionChanged
  906. // - SelectionModel receives CollectionChanged
  907. // - And so adjusts the selected item from 0 to 1, which is past the end of the items.
  908. //
  909. // There's not much we can do about this situation because the order in which
  910. // CollectionChanged handlers are called can't be known (the problem also exists with
  911. // WPF). The best we can do is not select an invalid index.
  912. var target = CreateTarget(createData: false);
  913. var data = new AvaloniaList<string>();
  914. data.CollectionChanged += (s, e) =>
  915. {
  916. target.Select(0);
  917. };
  918. target.Source = data;
  919. data.Add("foo");
  920. Assert.Equal(0, target.SelectedIndex);
  921. Assert.Equal(new[] { 0 }, target.SelectedIndexes);
  922. Assert.Equal("foo", target.SelectedItem);
  923. Assert.Equal(new[] { "foo" }, target.SelectedItems);
  924. Assert.Equal(0, target.AnchorIndex);
  925. }
  926. }
  927. public class BatchUpdate
  928. {
  929. [Fact]
  930. public void Changes_Do_Not_Take_Effect_Until_EndUpdate_Called()
  931. {
  932. var target = CreateTarget();
  933. target.BeginBatchUpdate();
  934. target.Select(0);
  935. Assert.Equal(-1, target.SelectedIndex);
  936. target.EndBatchUpdate();
  937. Assert.Equal(0, target.SelectedIndex);
  938. }
  939. [Fact]
  940. public void Correctly_Batches_Clear_SelectedIndex()
  941. {
  942. var target = CreateTarget();
  943. var raised = 0;
  944. target.SelectedIndex = 2;
  945. target.SelectionChanged += (s, e) => ++raised;
  946. using (target.BatchUpdate())
  947. {
  948. target.Clear();
  949. target.SelectedIndex = 2;
  950. }
  951. Assert.Equal(0, raised);
  952. }
  953. }
  954. public class LostSelection
  955. {
  956. [Fact]
  957. public void LostSelection_Called_On_Clear()
  958. {
  959. var target = CreateTarget();
  960. var raised = 0;
  961. target.SelectedIndex = 1;
  962. target.SelectionChanged += (s, e) =>
  963. {
  964. Assert.Equal(new[] { 1 }, e.DeselectedIndexes);
  965. Assert.Equal(new[] { "bar" }, e.DeselectedItems);
  966. Assert.Equal(new[] { 0 }, e.SelectedIndexes);
  967. Assert.Equal(new[] { "foo" }, e.SelectedItems);
  968. ++raised;
  969. };
  970. target.LostSelection += (s, e) =>
  971. {
  972. target.Select(0);
  973. };
  974. target.Clear();
  975. Assert.Equal(0, target.SelectedIndex);
  976. Assert.Equal(1, raised);
  977. }
  978. [Fact]
  979. public void LostSelection_Called_When_SelectedItem_Removed()
  980. {
  981. var target = CreateTarget();
  982. var data = (AvaloniaList<string>)target.Source!;
  983. var raised = 0;
  984. target.SelectedIndex = 1;
  985. target.SelectionChanged += (s, e) =>
  986. {
  987. Assert.Empty(e.DeselectedIndexes);
  988. Assert.Equal(new[] { "bar" }, e.DeselectedItems);
  989. Assert.Equal(new[] { 0 }, e.SelectedIndexes);
  990. Assert.Equal(new[] { "foo" }, e.SelectedItems);
  991. ++raised;
  992. };
  993. target.LostSelection += (s, e) =>
  994. {
  995. target.Select(0);
  996. };
  997. data.RemoveAt(1);
  998. Assert.Equal(0, target.SelectedIndex);
  999. Assert.Equal(1, raised);
  1000. }
  1001. [Fact]
  1002. public void LostSelection_Not_Called_With_Old_Source_When_Changing_Source()
  1003. {
  1004. var target = CreateTarget();
  1005. var data = (AvaloniaList<string>)target.Source!;
  1006. var raised = 0;
  1007. target.LostSelection += (s, e) =>
  1008. {
  1009. if (target.Source == data)
  1010. {
  1011. ++raised;
  1012. }
  1013. };
  1014. target.Source = null;
  1015. Assert.Equal(0, raised);
  1016. }
  1017. }
  1018. public class UntypedInterface
  1019. {
  1020. [Fact]
  1021. public void Raises_Untyped_SelectionChanged_Event()
  1022. {
  1023. var target = CreateTarget();
  1024. var raised = 0;
  1025. target.SelectedIndex = 1;
  1026. ((ISelectionModel)target).SelectionChanged += (s, e) =>
  1027. {
  1028. Assert.Equal(new[] { 1 }, e.DeselectedIndexes);
  1029. Assert.Equal(new[] { "bar" }, e.DeselectedItems);
  1030. Assert.Equal(new[] { 2 }, e.SelectedIndexes);
  1031. Assert.Equal(new[] { "baz" }, e.SelectedItems);
  1032. ++raised;
  1033. };
  1034. target.SelectedIndex = 2;
  1035. Assert.Equal(1, raised);
  1036. }
  1037. }
  1038. private static SelectionModel<string?> CreateTarget(bool createData = true)
  1039. {
  1040. var result = new SelectionModel<string?> { SingleSelect = true };
  1041. if (createData)
  1042. {
  1043. result.Source = new AvaloniaList<string> { "foo", "bar", "baz" };
  1044. }
  1045. return result;
  1046. }
  1047. }
  1048. }