SelectingItemsControlTests.cs 73 KB

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