SelectingItemsControlTests.cs 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Collections.Specialized;
  6. using System.ComponentModel;
  7. using System.Linq;
  8. using System.Reactive.Disposables;
  9. using System.Threading.Tasks;
  10. using Avalonia.Collections;
  11. using Avalonia.Controls.Presenters;
  12. using Avalonia.Controls.Primitives;
  13. using Avalonia.Controls.Selection;
  14. using Avalonia.Controls.Templates;
  15. using Avalonia.Data;
  16. using Avalonia.Input;
  17. using Avalonia.Input.Platform;
  18. using Avalonia.Interactivity;
  19. using Avalonia.Markup.Data;
  20. using Avalonia.Platform;
  21. using Avalonia.Styling;
  22. using Avalonia.Threading;
  23. using Avalonia.UnitTests;
  24. using Moq;
  25. using Xunit;
  26. namespace Avalonia.Controls.UnitTests.Primitives
  27. {
  28. public partial class SelectingItemsControlTests
  29. {
  30. private MouseTestHelper _helper = new MouseTestHelper();
  31. [Fact]
  32. public void SelectedIndex_Should_Initially_Be_Minus_1()
  33. {
  34. var items = new[]
  35. {
  36. new Item(),
  37. new Item(),
  38. };
  39. var target = new SelectingItemsControl
  40. {
  41. Items = items,
  42. Template = Template(),
  43. };
  44. Assert.Equal(-1, target.SelectedIndex);
  45. }
  46. [Fact]
  47. public void Item_IsSelected_Should_Initially_Be_False()
  48. {
  49. var items = new[]
  50. {
  51. new Item(),
  52. new Item(),
  53. };
  54. var target = new SelectingItemsControl
  55. {
  56. Items = items,
  57. Template = Template(),
  58. };
  59. Prepare(target);
  60. Assert.False(items[0].IsSelected);
  61. Assert.False(items[1].IsSelected);
  62. }
  63. [Fact]
  64. public void Setting_SelectedItem_Should_Set_Item_IsSelected_True()
  65. {
  66. var items = new[]
  67. {
  68. new Item(),
  69. new Item(),
  70. };
  71. var target = new SelectingItemsControl
  72. {
  73. Items = items,
  74. Template = Template(),
  75. };
  76. Prepare(target);
  77. target.SelectedItem = items[1];
  78. Assert.False(items[0].IsSelected);
  79. Assert.True(items[1].IsSelected);
  80. }
  81. [Fact]
  82. public void Setting_SelectedItem_Before_ApplyTemplate_Should_Set_Item_IsSelected_True()
  83. {
  84. var items = new[]
  85. {
  86. new Item(),
  87. new Item(),
  88. };
  89. var target = new SelectingItemsControl
  90. {
  91. Items = items,
  92. Template = Template(),
  93. };
  94. target.SelectedItem = items[1];
  95. Prepare(target);
  96. Assert.False(items[0].IsSelected);
  97. Assert.True(items[1].IsSelected);
  98. }
  99. [Fact]
  100. public void SelectedIndex_Should_Be_Minus_1_After_Initialize()
  101. {
  102. var items = new[]
  103. {
  104. new Item(),
  105. new Item(),
  106. };
  107. var target = new ListBox();
  108. target.BeginInit();
  109. target.Items = items;
  110. target.Template = Template();
  111. target.EndInit();
  112. Assert.Equal(-1, target.SelectedIndex);
  113. }
  114. [Fact]
  115. public void SelectedIndex_Should_Be_Minus_1_Without_Initialize()
  116. {
  117. var items = new[]
  118. {
  119. new Item(),
  120. new Item(),
  121. };
  122. var target = new ListBox();
  123. target.Items = items;
  124. target.Template = Template();
  125. target.DataContext = new object();
  126. Assert.Equal(-1, target.SelectedIndex);
  127. }
  128. [Fact]
  129. public void SelectedIndex_Should_Be_0_After_Initialize_With_AlwaysSelected()
  130. {
  131. var items = new[]
  132. {
  133. new Item(),
  134. new Item(),
  135. };
  136. var target = new ListBox();
  137. target.BeginInit();
  138. target.SelectionMode = SelectionMode.Single | SelectionMode.AlwaysSelected;
  139. target.Items = items;
  140. target.Template = Template();
  141. target.EndInit();
  142. Prepare(target);
  143. Assert.Equal(0, target.SelectedIndex);
  144. }
  145. [Fact]
  146. public void Setting_SelectedIndex_During_Initialize_Should_Select_Item_When_AlwaysSelected_Is_Used()
  147. {
  148. var listBox = new ListBox
  149. {
  150. SelectionMode = SelectionMode.Single | SelectionMode.AlwaysSelected
  151. };
  152. listBox.BeginInit();
  153. listBox.SelectedIndex = 1;
  154. var items = new AvaloniaList<string>();
  155. listBox.Items = items;
  156. items.Add("A");
  157. items.Add("B");
  158. items.Add("C");
  159. listBox.EndInit();
  160. Prepare(listBox);
  161. Assert.Equal("B", listBox.SelectedItem);
  162. }
  163. [Fact]
  164. public void Setting_SelectedIndex_Before_Initialize_Should_Retain_Selection()
  165. {
  166. var listBox = new ListBox
  167. {
  168. SelectionMode = SelectionMode.Single,
  169. Items = new[] { "foo", "bar", "baz" },
  170. SelectedIndex = 1
  171. };
  172. listBox.BeginInit();
  173. listBox.EndInit();
  174. Assert.Equal(1, listBox.SelectedIndex);
  175. Assert.Equal("bar", listBox.SelectedItem);
  176. }
  177. [Fact]
  178. public void Setting_SelectedIndex_During_Initialize_Should_Take_Priority_Over_Previous_Value()
  179. {
  180. var listBox = new ListBox
  181. {
  182. SelectionMode = SelectionMode.Single,
  183. Items = new[] { "foo", "bar", "baz" },
  184. SelectedIndex = 2
  185. };
  186. listBox.BeginInit();
  187. listBox.SelectedIndex = 1;
  188. listBox.EndInit();
  189. Assert.Equal(1, listBox.SelectedIndex);
  190. Assert.Equal("bar", listBox.SelectedItem);
  191. }
  192. [Fact]
  193. public void Setting_SelectedItem_Before_Initialize_Should_Retain_Selection()
  194. {
  195. var listBox = new ListBox
  196. {
  197. SelectionMode = SelectionMode.Single,
  198. Items = new[] { "foo", "bar", "baz" },
  199. SelectedItem = "bar"
  200. };
  201. listBox.BeginInit();
  202. listBox.EndInit();
  203. Assert.Equal(1, listBox.SelectedIndex);
  204. Assert.Equal("bar", listBox.SelectedItem);
  205. }
  206. [Fact]
  207. public void Setting_SelectedItems_Before_Initialize_Should_Retain_Selection()
  208. {
  209. var listBox = new ListBox
  210. {
  211. SelectionMode = SelectionMode.Multiple,
  212. Items = new[] { "foo", "bar", "baz" },
  213. };
  214. var selected = new[] { "foo", "bar" };
  215. foreach (var v in selected)
  216. {
  217. listBox.SelectedItems.Add(v);
  218. }
  219. listBox.BeginInit();
  220. listBox.EndInit();
  221. Assert.Equal(selected, listBox.SelectedItems);
  222. }
  223. [Fact]
  224. public void Setting_SelectedItems_During_Initialize_Should_Take_Priority_Over_Previous_Value()
  225. {
  226. var listBox = new ListBox
  227. {
  228. SelectionMode = SelectionMode.Multiple,
  229. Items = new[] { "foo", "bar", "baz" },
  230. };
  231. var selected = new[] { "foo", "bar" };
  232. foreach (var v in new[] { "bar", "baz" })
  233. {
  234. listBox.SelectedItems.Add(v);
  235. }
  236. listBox.BeginInit();
  237. listBox.SelectedItems = new AvaloniaList<object>(selected);
  238. listBox.EndInit();
  239. Assert.Equal(selected, listBox.SelectedItems);
  240. }
  241. [Fact]
  242. public void Setting_SelectedIndex_Before_Initialize_With_AlwaysSelected_Should_Retain_Selection()
  243. {
  244. var listBox = new ListBox
  245. {
  246. SelectionMode = SelectionMode.Single | SelectionMode.AlwaysSelected,
  247. Items = new[] { "foo", "bar", "baz" },
  248. SelectedIndex = 1
  249. };
  250. listBox.BeginInit();
  251. listBox.EndInit();
  252. Assert.Equal(1, listBox.SelectedIndex);
  253. Assert.Equal("bar", listBox.SelectedItem);
  254. }
  255. [Fact]
  256. public void Setting_SelectedIndex_Before_ApplyTemplate_Should_Set_Item_IsSelected_True()
  257. {
  258. var items = new[]
  259. {
  260. new Item(),
  261. new Item(),
  262. };
  263. var target = new SelectingItemsControl
  264. {
  265. Items = items,
  266. Template = Template(),
  267. };
  268. target.SelectedIndex = 1;
  269. Prepare(target);
  270. Assert.False(items[0].IsSelected);
  271. Assert.True(items[1].IsSelected);
  272. }
  273. [Fact]
  274. public void Setting_SelectedItem_Should_Set_SelectedIndex()
  275. {
  276. var items = new[]
  277. {
  278. new Item(),
  279. new Item(),
  280. };
  281. var target = new SelectingItemsControl
  282. {
  283. Items = items,
  284. Template = Template(),
  285. };
  286. target.ApplyTemplate();
  287. target.SelectedItem = items[1];
  288. Assert.Equal(items[1], target.SelectedItem);
  289. Assert.Equal(1, target.SelectedIndex);
  290. }
  291. [Fact]
  292. public void SelectedIndex_Item_Is_Updated_As_Items_Removed_When_Last_Item_Is_Selected()
  293. {
  294. var items = new ObservableCollection<string>
  295. {
  296. "Foo",
  297. "Bar",
  298. "FooBar"
  299. };
  300. var target = new SelectingItemsControl
  301. {
  302. Items = items,
  303. Template = Template(),
  304. };
  305. target.ApplyTemplate();
  306. target.SelectedItem = items[2];
  307. Assert.Equal(items[2], target.SelectedItem);
  308. Assert.Equal(2, target.SelectedIndex);
  309. items.RemoveAt(0);
  310. Assert.Equal(items[1], target.SelectedItem);
  311. Assert.Equal(1, target.SelectedIndex);
  312. }
  313. [Fact]
  314. public void Setting_SelectedItem_To_Not_Present_Item_Should_Clear_Selection()
  315. {
  316. var items = new[]
  317. {
  318. new Item(),
  319. new Item(),
  320. };
  321. var target = new SelectingItemsControl
  322. {
  323. Items = items,
  324. Template = Template(),
  325. };
  326. target.ApplyTemplate();
  327. target.SelectedItem = items[1];
  328. Assert.Equal(items[1], target.SelectedItem);
  329. Assert.Equal(1, target.SelectedIndex);
  330. target.SelectedItem = new Item();
  331. Assert.Null(target.SelectedItem);
  332. Assert.Equal(-1, target.SelectedIndex);
  333. }
  334. [Fact]
  335. public void Setting_SelectedIndex_Should_Set_SelectedItem()
  336. {
  337. var items = new[]
  338. {
  339. new Item(),
  340. new Item(),
  341. };
  342. var target = new SelectingItemsControl
  343. {
  344. Items = items,
  345. Template = Template(),
  346. };
  347. target.ApplyTemplate();
  348. target.SelectedIndex = 1;
  349. Assert.Equal(items[1], target.SelectedItem);
  350. }
  351. [Fact]
  352. public void Setting_SelectedIndex_Out_Of_Bounds_Should_Clear_Selection()
  353. {
  354. var items = new[]
  355. {
  356. new Item(),
  357. new Item(),
  358. };
  359. var target = new SelectingItemsControl
  360. {
  361. Items = items,
  362. Template = Template(),
  363. };
  364. target.ApplyTemplate();
  365. target.SelectedIndex = 2;
  366. Assert.Equal(-1, target.SelectedIndex);
  367. }
  368. [Fact]
  369. public void Setting_SelectedItem_To_Non_Existent_Item_Should_Clear_Selection()
  370. {
  371. var target = new SelectingItemsControl
  372. {
  373. Template = Template(),
  374. };
  375. target.ApplyTemplate();
  376. target.SelectedItem = new Item();
  377. Assert.Equal(-1, target.SelectedIndex);
  378. Assert.Null(target.SelectedItem);
  379. }
  380. [Fact]
  381. public void Adding_Selected_Item_Should_Update_Selection()
  382. {
  383. var items = new AvaloniaList<Item>(new[]
  384. {
  385. new Item(),
  386. new Item(),
  387. });
  388. var target = new SelectingItemsControl
  389. {
  390. Items = items,
  391. Template = Template(),
  392. };
  393. Prepare(target);
  394. items.Add(new Item { IsSelected = true });
  395. Assert.Equal(2, target.SelectedIndex);
  396. Assert.Equal(items[2], target.SelectedItem);
  397. }
  398. [Fact]
  399. public void Setting_Items_To_Null_Should_Clear_Selection()
  400. {
  401. var items = new AvaloniaList<Item>
  402. {
  403. new Item(),
  404. new Item(),
  405. };
  406. var target = new SelectingItemsControl
  407. {
  408. Items = items,
  409. Template = Template(),
  410. };
  411. target.ApplyTemplate();
  412. target.SelectedIndex = 1;
  413. Assert.Equal(items[1], target.SelectedItem);
  414. Assert.Equal(1, target.SelectedIndex);
  415. target.Items = null;
  416. Assert.Null(target.SelectedItem);
  417. Assert.Equal(-1, target.SelectedIndex);
  418. }
  419. [Fact]
  420. public void Removing_Selected_Item_Should_Clear_Selection()
  421. {
  422. var items = new AvaloniaList<Item>
  423. {
  424. new Item(),
  425. new Item(),
  426. };
  427. var target = new SelectingItemsControl
  428. {
  429. Items = items,
  430. Template = Template(),
  431. };
  432. Prepare(target);
  433. target.SelectedIndex = 1;
  434. Assert.Equal(items[1], target.SelectedItem);
  435. Assert.Equal(1, target.SelectedIndex);
  436. SelectionChangedEventArgs receivedArgs = null;
  437. target.SelectionChanged += (_, args) => receivedArgs = args;
  438. var removed = items[1];
  439. items.RemoveAt(1);
  440. Assert.Null(target.SelectedItem);
  441. Assert.Equal(-1, target.SelectedIndex);
  442. Assert.NotNull(receivedArgs);
  443. Assert.Empty(receivedArgs.AddedItems);
  444. Assert.Equal(new[] { removed }, receivedArgs.RemovedItems);
  445. Assert.False(items.Single().IsSelected);
  446. }
  447. [Fact]
  448. public void Removing_Selected_Item_Should_Update_Selection_With_AlwaysSelected()
  449. {
  450. var item0 = new Item();
  451. var item1 = new Item();
  452. var items = new AvaloniaList<Item>
  453. {
  454. item0,
  455. item1,
  456. };
  457. var target = new TestSelector
  458. {
  459. Items = items,
  460. Template = Template(),
  461. SelectionMode = SelectionMode.AlwaysSelected,
  462. };
  463. Prepare(target);
  464. target.SelectedIndex = 1;
  465. Assert.Equal(items[1], target.SelectedItem);
  466. Assert.Equal(1, target.SelectedIndex);
  467. SelectionChangedEventArgs receivedArgs = null;
  468. target.SelectionChanged += (_, args) => receivedArgs = args;
  469. items.RemoveAt(1);
  470. Assert.Same(item0, target.SelectedItem);
  471. Assert.Equal(0, target.SelectedIndex);
  472. Assert.NotNull(receivedArgs);
  473. Assert.Equal(new[] { item0 }, receivedArgs.AddedItems);
  474. Assert.Equal(new[] { item1 }, receivedArgs.RemovedItems);
  475. Assert.True(items.Single().IsSelected);
  476. }
  477. [Fact]
  478. public void Removing_Selected_Item_Should_Clear_Selection_With_BeginInit()
  479. {
  480. var items = new AvaloniaList<Item>
  481. {
  482. new Item(),
  483. new Item(),
  484. };
  485. var target = new SelectingItemsControl();
  486. target.BeginInit();
  487. target.Items = items;
  488. target.Template = Template();
  489. target.EndInit();
  490. Prepare(target);
  491. target.SelectedIndex = 0;
  492. Assert.Equal(items[0], target.SelectedItem);
  493. Assert.Equal(0, target.SelectedIndex);
  494. SelectionChangedEventArgs receivedArgs = null;
  495. target.SelectionChanged += (_, args) => receivedArgs = args;
  496. var removed = items[0];
  497. items.RemoveAt(0);
  498. Assert.Null(target.SelectedItem);
  499. Assert.Equal(-1, target.SelectedIndex);
  500. Assert.NotNull(receivedArgs);
  501. Assert.Empty(receivedArgs.AddedItems);
  502. Assert.Equal(new[] { removed }, receivedArgs.RemovedItems);
  503. Assert.False(items.Single().IsSelected);
  504. }
  505. [Fact]
  506. public void Resetting_Items_Collection_Should_Clear_Selection()
  507. {
  508. // Need to use ObservableCollection here as AvaloniaList signals a Clear as an
  509. // add + remove.
  510. var items = new ObservableCollection<Item>
  511. {
  512. new Item(),
  513. new Item(),
  514. };
  515. var target = new SelectingItemsControl
  516. {
  517. Items = items,
  518. Template = Template(),
  519. };
  520. target.ApplyTemplate();
  521. target.SelectedIndex = 1;
  522. Assert.Equal(items[1], target.SelectedItem);
  523. Assert.Equal(1, target.SelectedIndex);
  524. items.Clear();
  525. Assert.Null(target.SelectedItem);
  526. Assert.Equal(-1, target.SelectedIndex);
  527. }
  528. [Fact]
  529. public void Raising_IsSelectedChanged_On_Item_Should_Update_Selection()
  530. {
  531. var items = new[]
  532. {
  533. new Item(),
  534. new Item(),
  535. };
  536. var target = new SelectingItemsControl
  537. {
  538. Items = items,
  539. Template = Template(),
  540. };
  541. Prepare(target);
  542. target.SelectedItem = items[1];
  543. Assert.False(items[0].IsSelected);
  544. Assert.True(items[1].IsSelected);
  545. items[0].IsSelected = true;
  546. items[0].RaiseEvent(new RoutedEventArgs(SelectingItemsControl.IsSelectedChangedEvent));
  547. Assert.Equal(0, target.SelectedIndex);
  548. Assert.Equal(items[0], target.SelectedItem);
  549. Assert.True(items[0].IsSelected);
  550. Assert.False(items[1].IsSelected);
  551. }
  552. [Fact]
  553. public void Clearing_IsSelected_And_Raising_IsSelectedChanged_On_Item_Should_Update_Selection()
  554. {
  555. var items = new[]
  556. {
  557. new Item(),
  558. new Item(),
  559. };
  560. var target = new SelectingItemsControl
  561. {
  562. Items = items,
  563. Template = Template(),
  564. };
  565. Prepare(target);
  566. target.SelectedItem = items[1];
  567. Assert.False(items[0].IsSelected);
  568. Assert.True(items[1].IsSelected);
  569. items[1].IsSelected = false;
  570. items[1].RaiseEvent(new RoutedEventArgs(SelectingItemsControl.IsSelectedChangedEvent));
  571. Assert.Equal(-1, target.SelectedIndex);
  572. Assert.Null(target.SelectedItem);
  573. }
  574. [Fact]
  575. public void Raising_IsSelectedChanged_On_Someone_Elses_Item_Should_Not_Update_Selection()
  576. {
  577. var items = new[]
  578. {
  579. new Item(),
  580. new Item(),
  581. };
  582. var target = new SelectingItemsControl
  583. {
  584. Items = items,
  585. Template = Template(),
  586. };
  587. target.ApplyTemplate();
  588. target.SelectedItem = items[1];
  589. var notChild = new Item
  590. {
  591. IsSelected = true,
  592. };
  593. target.RaiseEvent(new RoutedEventArgs
  594. {
  595. RoutedEvent = SelectingItemsControl.IsSelectedChangedEvent,
  596. Source = notChild,
  597. });
  598. Assert.Equal(target.SelectedItem, items[1]);
  599. }
  600. [Fact]
  601. public void Setting_SelectedIndex_Should_Raise_SelectionChanged_Event()
  602. {
  603. var items = new[]
  604. {
  605. new Item(),
  606. new Item(),
  607. };
  608. var target = new SelectingItemsControl
  609. {
  610. Items = items,
  611. Template = Template(),
  612. };
  613. var called = false;
  614. target.SelectionChanged += (s, e) =>
  615. {
  616. Assert.Same(items[1], e.AddedItems.Cast<object>().Single());
  617. Assert.Empty(e.RemovedItems);
  618. called = true;
  619. };
  620. target.SelectedIndex = 1;
  621. Assert.True(called);
  622. }
  623. [Fact]
  624. public void Clearing_SelectedIndex_Should_Raise_SelectionChanged_Event()
  625. {
  626. var items = new[]
  627. {
  628. new Item(),
  629. new Item(),
  630. };
  631. var target = new SelectingItemsControl
  632. {
  633. Items = items,
  634. Template = Template(),
  635. SelectedIndex = 1,
  636. };
  637. Prepare(target);
  638. var called = false;
  639. target.SelectionChanged += (s, e) =>
  640. {
  641. Assert.Same(items[1], e.RemovedItems.Cast<object>().Single());
  642. Assert.Empty(e.AddedItems);
  643. called = true;
  644. };
  645. target.SelectedIndex = -1;
  646. Assert.True(called);
  647. }
  648. [Fact]
  649. public void Setting_SelectedIndex_Should_Raise_PropertyChanged_Events()
  650. {
  651. var items = new ObservableCollection<string> { "foo", "bar", "baz" };
  652. var target = new TestSelector
  653. {
  654. Items = items,
  655. Template = Template(),
  656. };
  657. var selectedIndexRaised = 0;
  658. var selectedItemRaised = 0;
  659. target.PropertyChanged += (s, e) =>
  660. {
  661. if (e.Property == SelectingItemsControl.SelectedIndexProperty)
  662. {
  663. Assert.Equal(-1, e.OldValue);
  664. Assert.Equal(1, e.NewValue);
  665. ++selectedIndexRaised;
  666. }
  667. else if (e.Property == SelectingItemsControl.SelectedItemProperty)
  668. {
  669. Assert.Null(e.OldValue);
  670. Assert.Equal("bar", e.NewValue);
  671. ++selectedItemRaised;
  672. }
  673. };
  674. target.SelectedIndex = 1;
  675. Assert.Equal(1, selectedIndexRaised);
  676. Assert.Equal(1, selectedItemRaised);
  677. }
  678. [Fact]
  679. public void Removing_Selected_Item_Should_Raise_PropertyChanged_Events()
  680. {
  681. var items = new ObservableCollection<string> { "foo", "bar", "baz" };
  682. var target = new TestSelector
  683. {
  684. Items = items,
  685. Template = Template(),
  686. };
  687. var selectedIndexRaised = 0;
  688. var selectedItemRaised = 0;
  689. target.SelectedIndex = 1;
  690. target.PropertyChanged += (s, e) =>
  691. {
  692. if (e.Property == SelectingItemsControl.SelectedIndexProperty)
  693. {
  694. Assert.Equal(1, e.OldValue);
  695. Assert.Equal(-1, e.NewValue);
  696. ++selectedIndexRaised;
  697. }
  698. else if (e.Property == SelectingItemsControl.SelectedItemProperty)
  699. {
  700. Assert.Equal("bar", e.OldValue);
  701. Assert.Null(e.NewValue);
  702. }
  703. };
  704. items.RemoveAt(1);
  705. Assert.Equal(1, selectedIndexRaised);
  706. Assert.Equal(0, selectedItemRaised);
  707. }
  708. [Fact]
  709. public void Removing_Selected_Item0_Should_Raise_PropertyChanged_Events_With_AlwaysSelected()
  710. {
  711. var items = new ObservableCollection<string> { "foo", "bar", "baz" };
  712. var target = new TestSelector
  713. {
  714. Items = items,
  715. Template = Template(),
  716. SelectionMode = SelectionMode.AlwaysSelected,
  717. };
  718. var selectedIndexRaised = 0;
  719. var selectedItemRaised = 0;
  720. target.SelectedIndex = 0;
  721. target.PropertyChanged += (s, e) =>
  722. {
  723. if (e.Property == SelectingItemsControl.SelectedIndexProperty)
  724. {
  725. ++selectedIndexRaised;
  726. }
  727. else if (e.Property == SelectingItemsControl.SelectedItemProperty)
  728. {
  729. Assert.Equal("foo", e.OldValue);
  730. Assert.Equal("bar", e.NewValue);
  731. ++selectedItemRaised;
  732. }
  733. };
  734. items.RemoveAt(0);
  735. Assert.Equal(0, selectedIndexRaised);
  736. Assert.Equal(1, selectedItemRaised);
  737. }
  738. [Fact]
  739. public void Removing_Selected_Item1_Should_Raise_PropertyChanged_Events_With_AlwaysSelected()
  740. {
  741. var items = new ObservableCollection<string> { "foo", "bar", "baz" };
  742. var target = new TestSelector
  743. {
  744. Items = items,
  745. Template = Template(),
  746. SelectionMode = SelectionMode.AlwaysSelected,
  747. };
  748. var selectedIndexRaised = 0;
  749. var selectedItemRaised = 0;
  750. target.SelectedIndex = 1;
  751. target.PropertyChanged += (s, e) =>
  752. {
  753. if (e.Property == SelectingItemsControl.SelectedIndexProperty)
  754. {
  755. Assert.Equal(1, e.OldValue);
  756. Assert.Equal(0, e.NewValue);
  757. ++selectedIndexRaised;
  758. }
  759. else if (e.Property == SelectingItemsControl.SelectedItemProperty)
  760. {
  761. Assert.Equal("bar", e.OldValue);
  762. Assert.Equal("foo", e.NewValue);
  763. }
  764. };
  765. items.RemoveAt(1);
  766. Assert.Equal(1, selectedIndexRaised);
  767. Assert.Equal(0, selectedItemRaised);
  768. }
  769. [Fact]
  770. public void Removing_Item_Before_Selection_Should_Raise_PropertyChanged_Events()
  771. {
  772. var items = new ObservableCollection<string> { "foo", "bar", "baz" };
  773. var target = new SelectingItemsControl
  774. {
  775. Items = items,
  776. Template = Template(),
  777. };
  778. var selectedIndexRaised = 0;
  779. var selectedItemRaised = 0;
  780. target.SelectedIndex = 1;
  781. target.PropertyChanged += (s, e) =>
  782. {
  783. if (e.Property == SelectingItemsControl.SelectedIndexProperty)
  784. {
  785. Assert.Equal(1, e.OldValue);
  786. Assert.Equal(0, e.NewValue);
  787. ++selectedIndexRaised;
  788. }
  789. else if (e.Property == SelectingItemsControl.SelectedItemProperty)
  790. {
  791. ++selectedItemRaised;
  792. }
  793. };
  794. items.RemoveAt(0);
  795. Assert.Equal(1, selectedIndexRaised);
  796. Assert.Equal(0, selectedItemRaised);
  797. }
  798. [Fact]
  799. public void Order_Of_Setting_Items_And_SelectedIndex_During_Initialization_Should_Not_Matter()
  800. {
  801. var items = new[] { "Foo", "Bar" };
  802. var target = new SelectingItemsControl();
  803. ((ISupportInitialize)target).BeginInit();
  804. target.SelectedIndex = 1;
  805. target.Items = items;
  806. ((ISupportInitialize)target).EndInit();
  807. Prepare(target);
  808. Assert.Equal(1, target.SelectedIndex);
  809. Assert.Equal("Bar", target.SelectedItem);
  810. }
  811. [Fact]
  812. public void Order_Of_Setting_Items_And_SelectedItem_During_Initialization_Should_Not_Matter()
  813. {
  814. var items = new[] { "Foo", "Bar" };
  815. var target = new SelectingItemsControl();
  816. ((ISupportInitialize)target).BeginInit();
  817. target.SelectedItem = "Bar";
  818. target.Items = items;
  819. ((ISupportInitialize)target).EndInit();
  820. Prepare(target);
  821. Assert.Equal(1, target.SelectedIndex);
  822. Assert.Equal("Bar", target.SelectedItem);
  823. }
  824. [Fact]
  825. public void Changing_DataContext_Should_Not_Clear_Nested_ViewModel_SelectedItem()
  826. {
  827. var items = new[]
  828. {
  829. new Item(),
  830. new Item(),
  831. };
  832. var vm = new MasterViewModel
  833. {
  834. Child = new ChildViewModel
  835. {
  836. Items = items,
  837. SelectedItem = items[1],
  838. }
  839. };
  840. var target = new SelectingItemsControl { DataContext = vm };
  841. var itemsBinding = new Binding("Child.Items");
  842. var selectedBinding = new Binding("Child.SelectedItem");
  843. target.Bind(SelectingItemsControl.ItemsProperty, itemsBinding);
  844. target.Bind(SelectingItemsControl.SelectedItemProperty, selectedBinding);
  845. Assert.Equal(1, target.SelectedIndex);
  846. Assert.Same(vm.Child.SelectedItem, target.SelectedItem);
  847. items = new[]
  848. {
  849. new Item { Value = "Item1" },
  850. new Item { Value = "Item2" },
  851. new Item { Value = "Item3" },
  852. };
  853. vm = new MasterViewModel
  854. {
  855. Child = new ChildViewModel
  856. {
  857. Items = items,
  858. SelectedItem = items[2],
  859. }
  860. };
  861. target.DataContext = vm;
  862. Assert.Equal(2, target.SelectedIndex);
  863. Assert.Same(vm.Child.SelectedItem, target.SelectedItem);
  864. }
  865. [Fact]
  866. public void Nested_ListBox_Does_Not_Change_Parent_SelectedIndex()
  867. {
  868. SelectingItemsControl nested;
  869. var root = new SelectingItemsControl
  870. {
  871. Template = Template(),
  872. Items = new IControl[]
  873. {
  874. new Border(),
  875. nested = new ListBox
  876. {
  877. Template = Template(),
  878. Items = new[] { "foo", "bar" },
  879. SelectedIndex = 1,
  880. }
  881. },
  882. SelectedIndex = 0,
  883. };
  884. root.ApplyTemplate();
  885. root.Presenter.ApplyTemplate();
  886. nested.ApplyTemplate();
  887. nested.Presenter.ApplyTemplate();
  888. Assert.Equal(0, root.SelectedIndex);
  889. Assert.Equal(1, nested.SelectedIndex);
  890. nested.SelectedIndex = 0;
  891. Assert.Equal(0, root.SelectedIndex);
  892. }
  893. [Fact]
  894. public void Setting_SelectedItem_With_Pointer_Should_Set_TabOnceActiveElement()
  895. {
  896. using (UnitTestApplication.Start())
  897. {
  898. var target = new ListBox
  899. {
  900. Template = Template(),
  901. Items = new[] { "Foo", "Bar", "Baz " },
  902. };
  903. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
  904. Prepare(target);
  905. _helper.Down((Interactive)target.Presenter.Panel.Children[1]);
  906. var panel = target.Presenter.Panel;
  907. Assert.Equal(
  908. KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel),
  909. panel.Children[1]);
  910. }
  911. }
  912. [Fact]
  913. public void Removing_SelectedItem_Should_Clear_TabOnceActiveElement()
  914. {
  915. using (UnitTestApplication.Start())
  916. {
  917. var items = new ObservableCollection<string>(new[] { "Foo", "Bar", "Baz " });
  918. var target = new ListBox
  919. {
  920. Template = Template(),
  921. Items = items,
  922. };
  923. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
  924. Prepare(target);
  925. _helper.Down(target.Presenter.Panel.Children[1]);
  926. items.RemoveAt(1);
  927. var panel = target.Presenter.Panel;
  928. Assert.Null(KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel));
  929. }
  930. }
  931. [Fact]
  932. public void Resetting_Items_Collection_Should_Retain_Selection()
  933. {
  934. var itemsMock = new Mock<List<string>>();
  935. var itemsMockAsINCC = itemsMock.As<INotifyCollectionChanged>();
  936. itemsMock.Object.AddRange(new[] { "Foo", "Bar", "Baz" });
  937. var target = new SelectingItemsControl
  938. {
  939. Items = itemsMock.Object
  940. };
  941. target.SelectedIndex = 1;
  942. itemsMockAsINCC.Raise(e => e.CollectionChanged += null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  943. Assert.True(target.SelectedIndex == 1);
  944. }
  945. [Fact]
  946. public void Binding_With_DelayedBinding_And_Initialization_Where_DataContext_Is_Root_Works()
  947. {
  948. // Test for #1932.
  949. var root = new RootWithItems();
  950. root.BeginInit();
  951. root.DataContext = root;
  952. var target = new ListBox();
  953. target.BeginInit();
  954. root.Child = target;
  955. DelayedBinding.Add(target, ItemsControl.ItemsProperty, new Binding(nameof(RootWithItems.Items)));
  956. DelayedBinding.Add(target, ListBox.SelectedItemProperty, new Binding(nameof(RootWithItems.Selected)));
  957. target.EndInit();
  958. root.EndInit();
  959. Assert.Equal("b", target.SelectedItem);
  960. }
  961. [Fact]
  962. public void Mode_For_SelectedIndex_Is_TwoWay_By_Default()
  963. {
  964. var items = new[]
  965. {
  966. new Item(),
  967. new Item(),
  968. new Item(),
  969. };
  970. var vm = new MasterViewModel
  971. {
  972. Child = new ChildViewModel
  973. {
  974. Items = items,
  975. SelectedIndex = 1,
  976. }
  977. };
  978. var target = new SelectingItemsControl { DataContext = vm };
  979. var itemsBinding = new Binding("Child.Items");
  980. var selectedIndBinding = new Binding("Child.SelectedIndex");
  981. target.Bind(SelectingItemsControl.ItemsProperty, itemsBinding);
  982. target.Bind(SelectingItemsControl.SelectedIndexProperty, selectedIndBinding);
  983. Assert.Equal(1, target.SelectedIndex);
  984. target.SelectedIndex = 2;
  985. Assert.Equal(2, target.SelectedIndex);
  986. Assert.Equal(2, vm.Child.SelectedIndex);
  987. }
  988. [Fact]
  989. public void Should_Select_Correct_Item_When_Duplicate_Items_Are_Present()
  990. {
  991. using (UnitTestApplication.Start())
  992. {
  993. var target = new ListBox
  994. {
  995. Template = Template(),
  996. Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
  997. };
  998. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
  999. Prepare(target);
  1000. _helper.Down((Interactive)target.Presenter.Panel.Children[3]);
  1001. Assert.Equal(3, target.SelectedIndex);
  1002. }
  1003. }
  1004. [Fact]
  1005. public void Should_Apply_Selected_Pseudoclass_To_Correct_Item_When_Duplicate_Items_Are_Present()
  1006. {
  1007. using (UnitTestApplication.Start())
  1008. {
  1009. var target = new ListBox
  1010. {
  1011. Template = Template(),
  1012. Items = new[] { "Foo", "Bar", "Baz", "Foo", "Bar", "Baz" },
  1013. };
  1014. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new Mock<PlatformHotkeyConfiguration>().Object);
  1015. Prepare(target);
  1016. _helper.Down((Interactive)target.Presenter.Panel.Children[3]);
  1017. Assert.Equal(new[] { ":pressed", ":selected" }, target.Presenter.Panel.Children[3].Classes);
  1018. }
  1019. }
  1020. [Fact]
  1021. public void Adding_Item_Before_SelectedItem_Should_Update_SelectedIndex()
  1022. {
  1023. var items = new ObservableCollection<string>
  1024. {
  1025. "Foo",
  1026. "Bar",
  1027. "Baz"
  1028. };
  1029. var target = new ListBox
  1030. {
  1031. Template = Template(),
  1032. Items = items,
  1033. SelectedIndex = 1,
  1034. };
  1035. Prepare(target);
  1036. items.Insert(0, "Qux");
  1037. Assert.Equal(2, target.SelectedIndex);
  1038. Assert.Equal("Bar", target.SelectedItem);
  1039. }
  1040. [Fact]
  1041. public void Removing_Item_Before_SelectedItem_Should_Update_SelectedIndex()
  1042. {
  1043. var items = new ObservableCollection<string>
  1044. {
  1045. "Foo",
  1046. "Bar",
  1047. "Baz"
  1048. };
  1049. var target = new ListBox
  1050. {
  1051. Template = Template(),
  1052. Items = items,
  1053. SelectedIndex = 1,
  1054. };
  1055. Prepare(target);
  1056. items.RemoveAt(0);
  1057. Assert.Equal(0, target.SelectedIndex);
  1058. Assert.Equal("Bar", target.SelectedItem);
  1059. }
  1060. [Fact]
  1061. public void Binding_SelectedIndex_Selects_Correct_Item()
  1062. {
  1063. // Issue #4496 (part 2)
  1064. var items = new ObservableCollection<string>();
  1065. var other = new ListBox
  1066. {
  1067. Template = Template(),
  1068. Items = items,
  1069. SelectionMode = SelectionMode.AlwaysSelected,
  1070. };
  1071. var target = new ListBox
  1072. {
  1073. Template = Template(),
  1074. Items = items,
  1075. [!ListBox.SelectedIndexProperty] = other[!ListBox.SelectedIndexProperty],
  1076. };
  1077. Prepare(other);
  1078. Prepare(target);
  1079. items.Add("Foo");
  1080. Assert.Equal(0, other.SelectedIndex);
  1081. Assert.Equal(0, target.SelectedIndex);
  1082. }
  1083. [Fact]
  1084. public void Binding_SelectedItem_Selects_Correct_Item()
  1085. {
  1086. // Issue #4496 (part 2)
  1087. var items = new ObservableCollection<string>();
  1088. var other = new ListBox
  1089. {
  1090. Template = Template(),
  1091. Items = items,
  1092. SelectionMode = SelectionMode.AlwaysSelected,
  1093. };
  1094. var target = new ListBox
  1095. {
  1096. Template = Template(),
  1097. Items = items,
  1098. [!ListBox.SelectedItemProperty] = other[!ListBox.SelectedItemProperty],
  1099. };
  1100. Prepare(target);
  1101. other.ApplyTemplate();
  1102. other.Presenter.ApplyTemplate();
  1103. items.Add("Foo");
  1104. Assert.Equal(0, other.SelectedIndex);
  1105. Assert.Equal(0, target.SelectedIndex);
  1106. }
  1107. [Fact]
  1108. public void Replacing_Selected_Item_Should_Update_SelectedItem()
  1109. {
  1110. var items = new ObservableCollection<string>
  1111. {
  1112. "Foo",
  1113. "Bar",
  1114. "Baz"
  1115. };
  1116. var target = new ListBox
  1117. {
  1118. Template = Template(),
  1119. Items = items,
  1120. SelectedIndex = 1,
  1121. };
  1122. Prepare(target);
  1123. items[1] = "Qux";
  1124. Assert.Equal(-1, target.SelectedIndex);
  1125. Assert.Null(target.SelectedItem);
  1126. }
  1127. [Fact]
  1128. public void AutoScrollToSelectedItem_Causes_Scroll_To_SelectedItem()
  1129. {
  1130. var items = new ObservableCollection<string>
  1131. {
  1132. "Foo",
  1133. "Bar",
  1134. "Baz"
  1135. };
  1136. var target = new ListBox
  1137. {
  1138. Template = Template(),
  1139. Items = items,
  1140. };
  1141. var raised = false;
  1142. Prepare(target);
  1143. target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true);
  1144. target.SelectedIndex = 2;
  1145. Assert.True(raised);
  1146. }
  1147. [Fact]
  1148. public void AutoScrollToSelectedItem_Causes_Scroll_To_Initial_SelectedItem()
  1149. {
  1150. var items = new ObservableCollection<string>
  1151. {
  1152. "Foo",
  1153. "Bar",
  1154. "Baz"
  1155. };
  1156. var target = new ListBox
  1157. {
  1158. Template = Template(),
  1159. Items = items,
  1160. };
  1161. var raised = false;
  1162. target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true);
  1163. target.SelectedIndex = 2;
  1164. Prepare(target);
  1165. Assert.True(raised);
  1166. }
  1167. [Fact]
  1168. public void AutoScrollToSelectedItem_On_Reset_Works()
  1169. {
  1170. // Issue #3148
  1171. using (UnitTestApplication.Start(TestServices.StyledWindow))
  1172. {
  1173. var items = new ResettingCollection(100);
  1174. var target = new ListBox
  1175. {
  1176. Items = items,
  1177. ItemTemplate = new FuncDataTemplate<string>((x, _) =>
  1178. new TextBlock
  1179. {
  1180. Text = x,
  1181. Width = 100,
  1182. Height = 10
  1183. }),
  1184. AutoScrollToSelectedItem = true,
  1185. VirtualizationMode = ItemVirtualizationMode.Simple,
  1186. };
  1187. var root = new TestRoot(true, target);
  1188. root.Measure(new Size(100, 100));
  1189. root.Arrange(new Rect(0, 0, 100, 100));
  1190. Assert.True(target.Presenter.Panel.Children.Count > 0);
  1191. Assert.True(target.Presenter.Panel.Children.Count < 100);
  1192. target.SelectedItem = "Item99";
  1193. // #3148 triggered here.
  1194. items.Reset(new[] { "Item99" });
  1195. Assert.Equal(0, target.SelectedIndex);
  1196. Assert.Equal(1, target.Presenter.Panel.Children.Count);
  1197. }
  1198. }
  1199. [Fact]
  1200. public void AutoScrollToSelectedItem_Scrolls_When_Reattached_To_Visual_Tree_If_Selection_Changed_While_Detached_From_Visual_Tree()
  1201. {
  1202. var items = new ObservableCollection<string>
  1203. {
  1204. "Foo",
  1205. "Bar",
  1206. "Baz"
  1207. };
  1208. var target = new ListBox
  1209. {
  1210. Template = Template(),
  1211. Items = items,
  1212. SelectedIndex = 2,
  1213. };
  1214. var raised = false;
  1215. Prepare(target);
  1216. var root = (TestRoot)target.Parent;
  1217. target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true);
  1218. root.Child = null;
  1219. target.SelectedIndex = 1;
  1220. root.Child = target;
  1221. Assert.True(raised);
  1222. }
  1223. [Fact]
  1224. public void AutoScrollToSelectedItem_Doesnt_Scroll_If_Reattached_To_Visual_Tree_With_No_Selection_Change()
  1225. {
  1226. var items = new ObservableCollection<string>
  1227. {
  1228. "Foo",
  1229. "Bar",
  1230. "Baz"
  1231. };
  1232. var target = new ListBox
  1233. {
  1234. Template = Template(),
  1235. Items = items,
  1236. SelectedIndex = 2,
  1237. };
  1238. var raised = false;
  1239. Prepare(target);
  1240. var root = (TestRoot)target.Parent;
  1241. target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true);
  1242. root.Child = null;
  1243. root.Child = target;
  1244. Assert.False(raised);
  1245. }
  1246. [Fact]
  1247. public void AutoScrollToSelectedItem_Causes_Scroll_When_Turned_On()
  1248. {
  1249. var items = new ObservableCollection<string>
  1250. {
  1251. "Foo",
  1252. "Bar",
  1253. "Baz"
  1254. };
  1255. var target = new ListBox
  1256. {
  1257. Template = Template(),
  1258. Items = items,
  1259. AutoScrollToSelectedItem = false,
  1260. };
  1261. Prepare(target);
  1262. var raised = false;
  1263. target.AddHandler(Control.RequestBringIntoViewEvent, (s, e) => raised = true);
  1264. target.SelectedIndex = 2;
  1265. Assert.False(raised);
  1266. target.AutoScrollToSelectedItem = true;
  1267. Assert.True(raised);
  1268. }
  1269. [Fact]
  1270. public void Can_Set_Both_SelectedItem_And_SelectedItems_During_Initialization()
  1271. {
  1272. // Issue #2969.
  1273. var target = new ListBox();
  1274. var selectedItems = new List<object>();
  1275. target.BeginInit();
  1276. target.Template = Template();
  1277. target.Items = new[] { "Foo", "Bar", "Baz" };
  1278. target.SelectedItems = selectedItems;
  1279. target.SelectedItem = "Bar";
  1280. target.EndInit();
  1281. Prepare(target);
  1282. Assert.Equal("Bar", target.SelectedItem);
  1283. Assert.Equal(1, target.SelectedIndex);
  1284. Assert.Same(selectedItems, target.SelectedItems);
  1285. Assert.Equal(new[] { "Bar" }, selectedItems);
  1286. }
  1287. [Fact(Timeout = 2000)]
  1288. public async Task MoveSelection_Wrap_Does_Not_Hang_With_No_Focusable_Controls()
  1289. {
  1290. // Issue #3094.
  1291. var target = new TestSelector
  1292. {
  1293. Template = Template(),
  1294. Items = new[]
  1295. {
  1296. new ListBoxItem { Focusable = false },
  1297. new ListBoxItem { Focusable = false },
  1298. },
  1299. SelectedIndex = 0,
  1300. };
  1301. target.Measure(new Size(100, 100));
  1302. target.Arrange(new Rect(0, 0, 100, 100));
  1303. // Timeout in xUnit doesn't work with synchronous methods so we need to apply hack below.
  1304. // https://github.com/xunit/xunit/issues/2222
  1305. await Task.Run(() => target.MoveSelection(NavigationDirection.Next, true));
  1306. }
  1307. [Fact]
  1308. public void MoveSelection_Skips_Non_Focusable_Controls_When_Moving_To_Last_Item()
  1309. {
  1310. var target = new TestSelector
  1311. {
  1312. Template = Template(),
  1313. Items = new[]
  1314. {
  1315. new ListBoxItem(),
  1316. new ListBoxItem { Focusable = false },
  1317. }
  1318. };
  1319. target.Measure(new Size(100, 100));
  1320. target.Arrange(new Rect(0, 0, 100, 100));
  1321. target.MoveSelection(NavigationDirection.Last, true);
  1322. Assert.Equal(0, target.SelectedIndex);
  1323. }
  1324. [Fact]
  1325. public void MoveSelection_Skips_Non_Focusable_Controls_When_Moving_To_First_Item()
  1326. {
  1327. var target = new TestSelector
  1328. {
  1329. Template = Template(),
  1330. Items = new[]
  1331. {
  1332. new ListBoxItem { Focusable = false },
  1333. new ListBoxItem(),
  1334. }
  1335. };
  1336. target.Measure(new Size(100, 100));
  1337. target.Arrange(new Rect(0, 0, 100, 100));
  1338. target.MoveSelection(NavigationDirection.Last, true);
  1339. Assert.Equal(1, target.SelectedIndex);
  1340. }
  1341. [Fact(Timeout = 2000)]
  1342. public async Task MoveSelection_Does_Not_Hang_When_All_Items_Are_Non_Focusable_And_We_Move_To_First_Item()
  1343. {
  1344. var target = new TestSelector
  1345. {
  1346. Template = Template(),
  1347. Items = new[]
  1348. {
  1349. new ListBoxItem { Focusable = false },
  1350. new ListBoxItem { Focusable = false },
  1351. }
  1352. };
  1353. target.Measure(new Size(100, 100));
  1354. target.Arrange(new Rect(0, 0, 100, 100));
  1355. // Timeout in xUnit doesn't work with synchronous methods so we need to apply hack below.
  1356. // https://github.com/xunit/xunit/issues/2222
  1357. await Task.Run(() => target.MoveSelection(NavigationDirection.First, true));
  1358. Assert.Equal(-1, target.SelectedIndex);
  1359. }
  1360. [Fact(Timeout = 2000)]
  1361. public async Task MoveSelection_Does_Not_Hang_When_All_Items_Are_Non_Focusable_And_We_Move_To_Last_Item()
  1362. {
  1363. var target = new TestSelector
  1364. {
  1365. Template = Template(),
  1366. Items = new[]
  1367. {
  1368. new ListBoxItem { Focusable = false },
  1369. new ListBoxItem { Focusable = false },
  1370. }
  1371. };
  1372. target.Measure(new Size(100, 100));
  1373. target.Arrange(new Rect(0, 0, 100, 100));
  1374. // Timeout in xUnit doesn't work with synchronous methods so we need to apply hack below.
  1375. // https://github.com/xunit/xunit/issues/2222
  1376. await Task.Run(() => target.MoveSelection(NavigationDirection.Last, true));
  1377. Assert.Equal(-1, target.SelectedIndex);
  1378. }
  1379. [Fact]
  1380. public void MoveSelection_Does_Select_Disabled_Controls()
  1381. {
  1382. // Issue #3426.
  1383. var target = new TestSelector
  1384. {
  1385. Template = Template(),
  1386. Items = new[]
  1387. {
  1388. new ListBoxItem(),
  1389. new ListBoxItem { IsEnabled = false },
  1390. },
  1391. SelectedIndex = 0,
  1392. };
  1393. target.Measure(new Size(100, 100));
  1394. target.Arrange(new Rect(0, 0, 100, 100));
  1395. target.MoveSelection(NavigationDirection.Next, true);
  1396. Assert.Equal(0, target.SelectedIndex);
  1397. }
  1398. [Fact]
  1399. public void Pre_Selecting_Item_Should_Set_Selection_After_It_Was_Added_When_AlwaysSelected()
  1400. {
  1401. var target = new TestSelector(SelectionMode.AlwaysSelected)
  1402. {
  1403. Template = Template()
  1404. };
  1405. var second = new Item { IsSelected = true };
  1406. var items = new AvaloniaList<object>
  1407. {
  1408. new Item(),
  1409. second
  1410. };
  1411. target.Items = items;
  1412. Prepare(target);
  1413. Assert.Equal(second, target.SelectedItem);
  1414. Assert.Equal(1, target.SelectedIndex);
  1415. }
  1416. [Fact]
  1417. public void Setting_SelectionMode_Should_Update_SelectionModel()
  1418. {
  1419. var target = new TestSelector();
  1420. var model = target.Selection;
  1421. Assert.True(model.SingleSelect);
  1422. target.SelectionMode = SelectionMode.Multiple;
  1423. Assert.False(model.SingleSelect);
  1424. }
  1425. [Fact]
  1426. public void Does_The_Best_It_Can_With_AutoSelecting_ViewModel()
  1427. {
  1428. // Tests the following scenario:
  1429. //
  1430. // - Items changes from empty to having 1 item
  1431. // - ViewModel auto-selects item 0 in CollectionChanged
  1432. // - SelectionModel receives CollectionChanged
  1433. // - And so adjusts the selected item from 0 to 1, which is past the end of the items.
  1434. //
  1435. // There's not much we can do about this situation because the order in which
  1436. // CollectionChanged handlers are called can't be known (the problem also exists with
  1437. // WPF). The best we can do is not select an invalid index.
  1438. var vm = new SelectionViewModel();
  1439. vm.Items.CollectionChanged += (s, e) =>
  1440. {
  1441. if (vm.SelectedIndex == -1 && vm.Items.Count > 0)
  1442. {
  1443. vm.SelectedIndex = 0;
  1444. }
  1445. };
  1446. var target = new ListBox
  1447. {
  1448. [!ListBox.ItemsProperty] = new Binding("Items"),
  1449. [!ListBox.SelectedIndexProperty] = new Binding("SelectedIndex"),
  1450. DataContext = vm,
  1451. };
  1452. Prepare(target);
  1453. vm.Items.Add("foo");
  1454. vm.Items.Add("bar");
  1455. Assert.Equal(0, target.SelectedIndex);
  1456. Assert.Equal(new[] { 0 }, target.Selection.SelectedIndexes);
  1457. Assert.Equal("foo", target.SelectedItem);
  1458. Assert.Equal(new[] { "foo" }, target.SelectedItems);
  1459. }
  1460. [Fact]
  1461. public void Preserves_Initial_SelectedItems_When_Bound()
  1462. {
  1463. // Issue #4272 (there are two issues there, this addresses the second one).
  1464. var vm = new SelectionViewModel
  1465. {
  1466. Items = { "foo", "bar", "baz" },
  1467. SelectedItems = { "bar" },
  1468. };
  1469. var target = new ListBox
  1470. {
  1471. [!ListBox.ItemsProperty] = new Binding("Items"),
  1472. [!ListBox.SelectedItemsProperty] = new Binding("SelectedItems"),
  1473. DataContext = vm,
  1474. };
  1475. Prepare(target);
  1476. Assert.Equal(1, target.SelectedIndex);
  1477. Assert.Equal(new[] { 1 }, target.Selection.SelectedIndexes);
  1478. Assert.Equal("bar", target.SelectedItem);
  1479. Assert.Equal(new[] { "bar" }, target.SelectedItems);
  1480. }
  1481. [Fact]
  1482. public void Preserves_SelectedItem_When_Items_Changed()
  1483. {
  1484. // Issue #4048
  1485. var target = new SelectingItemsControl
  1486. {
  1487. Items = new[] { "foo", "bar", "baz"},
  1488. SelectedItem = "bar",
  1489. };
  1490. Prepare(target);
  1491. Assert.Equal(1, target.SelectedIndex);
  1492. Assert.Equal("bar", target.SelectedItem);
  1493. target.Items = new[] { "qux", "foo", "bar" };
  1494. Assert.Equal(2, target.SelectedIndex);
  1495. Assert.Equal("bar", target.SelectedItem);
  1496. }
  1497. [Fact]
  1498. public void Setting_SelectedItems_Raises_PropertyChanged()
  1499. {
  1500. var target = new TestSelector
  1501. {
  1502. Items = new[] { "foo", "bar", "baz" },
  1503. };
  1504. var raised = 0;
  1505. var newValue = new AvaloniaList<object>();
  1506. Prepare(target);
  1507. target.PropertyChanged += (s, e) =>
  1508. {
  1509. if (e.Property == ListBox.SelectedItemsProperty)
  1510. {
  1511. Assert.Null(e.OldValue);
  1512. Assert.Same(newValue, e.NewValue);
  1513. ++raised;
  1514. }
  1515. };
  1516. target.SelectedItems = newValue;
  1517. Assert.Equal(1, raised);
  1518. }
  1519. [Fact]
  1520. public void Setting_Selection_Raises_SelectedItems_PropertyChanged()
  1521. {
  1522. var target = new TestSelector
  1523. {
  1524. Items = new[] { "foo", "bar", "baz" },
  1525. };
  1526. var raised = 0;
  1527. var oldValue = target.SelectedItems;
  1528. Prepare(target);
  1529. target.PropertyChanged += (s, e) =>
  1530. {
  1531. if (e.Property == ListBox.SelectedItemsProperty)
  1532. {
  1533. Assert.Same(oldValue, e.OldValue);
  1534. Assert.Null(e.NewValue);
  1535. ++raised;
  1536. }
  1537. };
  1538. target.Selection = new SelectionModel<int>();
  1539. Assert.Equal(1, raised);
  1540. }
  1541. [Fact]
  1542. public void Handles_Removing_Last_Item_In_Two_Controls_With_Bound_SelectedIndex()
  1543. {
  1544. var items = new ObservableCollection<string> { "foo" };
  1545. // Simulates problem with TabStrip and Carousel with bound SelectedIndex.
  1546. var tabStrip = new TestSelector
  1547. {
  1548. Items = items,
  1549. SelectionMode = SelectionMode.AlwaysSelected,
  1550. };
  1551. var carousel = new TestSelector
  1552. {
  1553. Items = items,
  1554. [!Carousel.SelectedIndexProperty] = tabStrip[!TabStrip.SelectedIndexProperty],
  1555. };
  1556. var tabStripRaised = 0;
  1557. var carouselRaised = 0;
  1558. tabStrip.SelectionChanged += (s, e) =>
  1559. {
  1560. Assert.Equal(new[] { "foo" }, e.RemovedItems);
  1561. Assert.Empty(e.AddedItems);
  1562. ++tabStripRaised;
  1563. };
  1564. carousel.SelectionChanged += (s, e) =>
  1565. {
  1566. Assert.Equal(new[] { "foo" }, e.RemovedItems);
  1567. Assert.Empty(e.AddedItems);
  1568. ++carouselRaised;
  1569. };
  1570. items.RemoveAt(0);
  1571. Assert.Equal(1, tabStripRaised);
  1572. Assert.Equal(1, carouselRaised);
  1573. }
  1574. [Fact]
  1575. public void Handles_Removing_Last_Item_In_Controls_With_Bound_SelectedItem()
  1576. {
  1577. var items = new ObservableCollection<string> { "foo" };
  1578. // Simulates problem with TabStrip and Carousel with bound SelectedItem.
  1579. var tabStrip = new TestSelector
  1580. {
  1581. Items = items,
  1582. SelectionMode = SelectionMode.AlwaysSelected,
  1583. };
  1584. var carousel = new TestSelector
  1585. {
  1586. Items = items,
  1587. [!Carousel.SelectedItemProperty] = tabStrip[!TabStrip.SelectedItemProperty],
  1588. };
  1589. var tabStripRaised = 0;
  1590. var carouselRaised = 0;
  1591. tabStrip.SelectionChanged += (s, e) =>
  1592. {
  1593. Assert.Equal(new[] { "foo" }, e.RemovedItems);
  1594. Assert.Empty(e.AddedItems);
  1595. ++tabStripRaised;
  1596. };
  1597. carousel.SelectionChanged += (s, e) =>
  1598. {
  1599. Assert.Equal(new[] { "foo" }, e.RemovedItems);
  1600. Assert.Empty(e.AddedItems);
  1601. ++carouselRaised;
  1602. };
  1603. items.RemoveAt(0);
  1604. Assert.Equal(1, tabStripRaised);
  1605. Assert.Equal(1, carouselRaised);
  1606. }
  1607. [Fact]
  1608. public void Setting_IsTextSearchEnabled_Enables_Or_Disables_Text_Search()
  1609. {
  1610. var pti = Mock.Of<IPlatformThreadingInterface>(x => x.CurrentThreadIsLoopThread == true);
  1611. Mock.Get(pti)
  1612. .Setup(v => v.StartTimer(It.IsAny<DispatcherPriority>(), It.IsAny<TimeSpan>(), It.IsAny<Action>()))
  1613. .Returns(Disposable.Empty);
  1614. using (UnitTestApplication.Start(TestServices.StyledWindow.With(threadingInterface: pti)))
  1615. {
  1616. var items = new[]
  1617. {
  1618. new Item { [TextSearch.TextProperty] = "Foo" },
  1619. new Item { [TextSearch.TextProperty] = "Bar" }
  1620. };
  1621. var target = new SelectingItemsControl
  1622. {
  1623. Items = items,
  1624. Template = Template(),
  1625. IsTextSearchEnabled = false
  1626. };
  1627. Prepare(target);
  1628. target.RaiseEvent(new TextInputEventArgs
  1629. {
  1630. RoutedEvent = InputElement.TextInputEvent,
  1631. Device = KeyboardDevice.Instance,
  1632. Text = "Foo"
  1633. });
  1634. Assert.Null(target.SelectedItem);
  1635. target.IsTextSearchEnabled = true;
  1636. target.RaiseEvent(new TextInputEventArgs
  1637. {
  1638. RoutedEvent = InputElement.TextInputEvent,
  1639. Device = KeyboardDevice.Instance,
  1640. Text = "Foo"
  1641. });
  1642. Assert.Equal(items[0], target.SelectedItem);
  1643. }
  1644. }
  1645. private static void Prepare(SelectingItemsControl target)
  1646. {
  1647. var root = new TestRoot
  1648. {
  1649. Child = target,
  1650. Width = 100,
  1651. Height = 100,
  1652. Styles =
  1653. {
  1654. new Style(x => x.Is<SelectingItemsControl>())
  1655. {
  1656. Setters =
  1657. {
  1658. new Setter(ListBox.TemplateProperty, Template()),
  1659. },
  1660. },
  1661. },
  1662. };
  1663. root.LayoutManager.ExecuteInitialLayoutPass();
  1664. }
  1665. private static FuncControlTemplate Template()
  1666. {
  1667. return new FuncControlTemplate<SelectingItemsControl>((control, scope) =>
  1668. new ItemsPresenter
  1669. {
  1670. Name = "itemsPresenter",
  1671. [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
  1672. [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty],
  1673. [~ItemsPresenter.VirtualizationModeProperty] = control[~ListBox.VirtualizationModeProperty],
  1674. }.RegisterInNameScope(scope));
  1675. }
  1676. private class Item : Control, ISelectable
  1677. {
  1678. public string Value { get; set; }
  1679. public bool IsSelected { get; set; }
  1680. }
  1681. private class MasterViewModel : NotifyingBase
  1682. {
  1683. private ChildViewModel _child;
  1684. public ChildViewModel Child
  1685. {
  1686. get { return _child; }
  1687. set
  1688. {
  1689. _child = value;
  1690. RaisePropertyChanged();
  1691. }
  1692. }
  1693. }
  1694. private class ChildViewModel : NotifyingBase
  1695. {
  1696. public IList<Item> Items { get; set; }
  1697. public Item SelectedItem { get; set; }
  1698. public int SelectedIndex { get; set; }
  1699. }
  1700. private class SelectionViewModel : NotifyingBase
  1701. {
  1702. private int _selectedIndex = -1;
  1703. public SelectionViewModel()
  1704. {
  1705. Items = new ObservableCollection<string>();
  1706. SelectedItems = new ObservableCollection<string>();
  1707. }
  1708. public int SelectedIndex
  1709. {
  1710. get => _selectedIndex;
  1711. set
  1712. {
  1713. _selectedIndex = value;
  1714. RaisePropertyChanged();
  1715. }
  1716. }
  1717. public ObservableCollection<string> Items { get; }
  1718. public ObservableCollection<string> SelectedItems { get; }
  1719. }
  1720. private class RootWithItems : TestRoot
  1721. {
  1722. public List<string> Items { get; set; } = new List<string>() { "a", "b", "c", "d", "e" };
  1723. public string Selected { get; set; } = "b";
  1724. }
  1725. private class TestSelector : SelectingItemsControl
  1726. {
  1727. public TestSelector()
  1728. {
  1729. }
  1730. public TestSelector(SelectionMode selectionMode)
  1731. {
  1732. SelectionMode = selectionMode;
  1733. }
  1734. public new ISelectionModel Selection
  1735. {
  1736. get => base.Selection;
  1737. set => base.Selection = value;
  1738. }
  1739. public new IList SelectedItems
  1740. {
  1741. get => base.SelectedItems;
  1742. set => base.SelectedItems = value;
  1743. }
  1744. public new SelectionMode SelectionMode
  1745. {
  1746. get => base.SelectionMode;
  1747. set => base.SelectionMode = value;
  1748. }
  1749. public new bool MoveSelection(NavigationDirection direction, bool wrap)
  1750. {
  1751. return base.MoveSelection(direction, wrap);
  1752. }
  1753. }
  1754. private class ResettingCollection : List<string>, INotifyCollectionChanged
  1755. {
  1756. public ResettingCollection(int itemCount)
  1757. {
  1758. AddRange(Enumerable.Range(0, itemCount).Select(x => $"Item{x}"));
  1759. }
  1760. public void Reset(IEnumerable<string> items)
  1761. {
  1762. Clear();
  1763. AddRange(items);
  1764. CollectionChanged?.Invoke(
  1765. this,
  1766. new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  1767. }
  1768. public event NotifyCollectionChangedEventHandler CollectionChanged;
  1769. }
  1770. }
  1771. }