SelectingItemsControlTests.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System.Collections.Generic;
  4. using System.Collections.ObjectModel;
  5. using System.Collections.Specialized;
  6. using System.Linq;
  7. using Avalonia.Collections;
  8. using Avalonia.Controls.Presenters;
  9. using Avalonia.Controls.Primitives;
  10. using Avalonia.Controls.Templates;
  11. using Avalonia.Data;
  12. using Avalonia.Input;
  13. using Avalonia.Interactivity;
  14. using Avalonia.Markup.Data;
  15. using Avalonia.UnitTests;
  16. using Moq;
  17. using Xunit;
  18. namespace Avalonia.Controls.UnitTests.Primitives
  19. {
  20. public class SelectingItemsControlTests
  21. {
  22. [Fact]
  23. public void SelectedIndex_Should_Initially_Be_Minus_1()
  24. {
  25. var items = new[]
  26. {
  27. new Item(),
  28. new Item(),
  29. };
  30. var target = new SelectingItemsControl
  31. {
  32. Items = items,
  33. Template = Template(),
  34. };
  35. Assert.Equal(-1, target.SelectedIndex);
  36. }
  37. [Fact]
  38. public void Item_IsSelected_Should_Initially_Be_False()
  39. {
  40. var items = new[]
  41. {
  42. new Item(),
  43. new Item(),
  44. };
  45. var target = new SelectingItemsControl
  46. {
  47. Items = items,
  48. Template = Template(),
  49. };
  50. target.ApplyTemplate();
  51. Assert.False(items[0].IsSelected);
  52. Assert.False(items[1].IsSelected);
  53. }
  54. [Fact]
  55. public void Setting_SelectedItem_Should_Set_Item_IsSelected_True()
  56. {
  57. var items = new[]
  58. {
  59. new Item(),
  60. new Item(),
  61. };
  62. var target = new SelectingItemsControl
  63. {
  64. Items = items,
  65. Template = Template(),
  66. };
  67. target.ApplyTemplate();
  68. target.Presenter.ApplyTemplate();
  69. target.SelectedItem = items[1];
  70. Assert.False(items[0].IsSelected);
  71. Assert.True(items[1].IsSelected);
  72. }
  73. [Fact]
  74. public void Setting_SelectedItem_Before_ApplyTemplate_Should_Set_Item_IsSelected_True()
  75. {
  76. var items = new[]
  77. {
  78. new Item(),
  79. new Item(),
  80. };
  81. var target = new SelectingItemsControl
  82. {
  83. Items = items,
  84. Template = Template(),
  85. };
  86. target.SelectedItem = items[1];
  87. target.ApplyTemplate();
  88. target.Presenter.ApplyTemplate();
  89. Assert.False(items[0].IsSelected);
  90. Assert.True(items[1].IsSelected);
  91. }
  92. [Fact]
  93. public void Setting_SelectedIndex_Before_ApplyTemplate_Should_Set_Item_IsSelected_True()
  94. {
  95. var items = new[]
  96. {
  97. new Item(),
  98. new Item(),
  99. };
  100. var target = new SelectingItemsControl
  101. {
  102. Items = items,
  103. Template = Template(),
  104. };
  105. target.SelectedIndex = 1;
  106. target.ApplyTemplate();
  107. target.Presenter.ApplyTemplate();
  108. Assert.False(items[0].IsSelected);
  109. Assert.True(items[1].IsSelected);
  110. }
  111. [Fact]
  112. public void Setting_SelectedItem_Should_Set_SelectedIndex()
  113. {
  114. var items = new[]
  115. {
  116. new Item(),
  117. new Item(),
  118. };
  119. var target = new SelectingItemsControl
  120. {
  121. Items = items,
  122. Template = Template(),
  123. };
  124. target.ApplyTemplate();
  125. target.SelectedItem = items[1];
  126. Assert.Equal(items[1], target.SelectedItem);
  127. Assert.Equal(1, target.SelectedIndex);
  128. }
  129. [Fact]
  130. public void SelectedIndex_Item_Is_Updated_As_Items_Removed_When_Last_Item_Is_Selected()
  131. {
  132. var items = new ObservableCollection<string>
  133. {
  134. "Foo",
  135. "Bar",
  136. "FooBar"
  137. };
  138. var target = new SelectingItemsControl
  139. {
  140. Items = items,
  141. Template = Template(),
  142. };
  143. target.ApplyTemplate();
  144. target.SelectedItem = items[2];
  145. Assert.Equal(items[2], target.SelectedItem);
  146. Assert.Equal(2, target.SelectedIndex);
  147. items.RemoveAt(0);
  148. Assert.Equal(items[1], target.SelectedItem);
  149. Assert.Equal(1, target.SelectedIndex);
  150. }
  151. [Fact]
  152. public void Setting_SelectedItem_To_Not_Present_Item_Should_Clear_Selection()
  153. {
  154. var items = new[]
  155. {
  156. new Item(),
  157. new Item(),
  158. };
  159. var target = new SelectingItemsControl
  160. {
  161. Items = items,
  162. Template = Template(),
  163. };
  164. target.ApplyTemplate();
  165. target.SelectedItem = items[1];
  166. Assert.Equal(items[1], target.SelectedItem);
  167. Assert.Equal(1, target.SelectedIndex);
  168. target.SelectedItem = new Item();
  169. Assert.Null(target.SelectedItem);
  170. Assert.Equal(-1, target.SelectedIndex);
  171. }
  172. [Fact]
  173. public void Setting_SelectedIndex_Should_Set_SelectedItem()
  174. {
  175. var items = new[]
  176. {
  177. new Item(),
  178. new Item(),
  179. };
  180. var target = new SelectingItemsControl
  181. {
  182. Items = items,
  183. Template = Template(),
  184. };
  185. target.ApplyTemplate();
  186. target.SelectedIndex = 1;
  187. Assert.Equal(items[1], target.SelectedItem);
  188. }
  189. [Fact]
  190. public void Setting_SelectedIndex_Out_Of_Bounds_Should_Clear_Selection()
  191. {
  192. var items = new[]
  193. {
  194. new Item(),
  195. new Item(),
  196. };
  197. var target = new SelectingItemsControl
  198. {
  199. Items = items,
  200. Template = Template(),
  201. };
  202. target.ApplyTemplate();
  203. target.SelectedIndex = 2;
  204. Assert.Equal(-1, target.SelectedIndex);
  205. }
  206. [Fact]
  207. public void Setting_SelectedItem_To_Non_Existent_Item_Should_Clear_Selection()
  208. {
  209. var target = new SelectingItemsControl
  210. {
  211. Template = Template(),
  212. };
  213. target.ApplyTemplate();
  214. target.SelectedItem = new Item();
  215. Assert.Equal(-1, target.SelectedIndex);
  216. Assert.Null(target.SelectedItem);
  217. }
  218. [Fact]
  219. public void Adding_Selected_Item_Should_Update_Selection()
  220. {
  221. var items = new AvaloniaList<Item>(new[]
  222. {
  223. new Item(),
  224. new Item(),
  225. });
  226. var target = new SelectingItemsControl
  227. {
  228. Items = items,
  229. Template = Template(),
  230. };
  231. target.ApplyTemplate();
  232. target.Presenter.ApplyTemplate();
  233. items.Add(new Item { IsSelected = true });
  234. Assert.Equal(2, target.SelectedIndex);
  235. Assert.Equal(items[2], target.SelectedItem);
  236. }
  237. [Fact]
  238. public void Setting_Items_To_Null_Should_Clear_Selection()
  239. {
  240. var items = new AvaloniaList<Item>
  241. {
  242. new Item(),
  243. new Item(),
  244. };
  245. var target = new SelectingItemsControl
  246. {
  247. Items = items,
  248. Template = Template(),
  249. };
  250. target.ApplyTemplate();
  251. target.SelectedIndex = 1;
  252. Assert.Equal(items[1], target.SelectedItem);
  253. Assert.Equal(1, target.SelectedIndex);
  254. target.Items = null;
  255. Assert.Null(target.SelectedItem);
  256. Assert.Equal(-1, target.SelectedIndex);
  257. }
  258. [Fact]
  259. public void Removing_Selected_Item_Should_Clear_Selection()
  260. {
  261. var items = new AvaloniaList<Item>
  262. {
  263. new Item(),
  264. new Item(),
  265. };
  266. var target = new SelectingItemsControl
  267. {
  268. Items = items,
  269. Template = Template(),
  270. };
  271. target.ApplyTemplate();
  272. target.SelectedIndex = 1;
  273. Assert.Equal(items[1], target.SelectedItem);
  274. Assert.Equal(1, target.SelectedIndex);
  275. items.RemoveAt(1);
  276. Assert.Null(target.SelectedItem);
  277. Assert.Equal(-1, target.SelectedIndex);
  278. }
  279. [Fact]
  280. public void Resetting_Items_Collection_Should_Clear_Selection()
  281. {
  282. // Need to use ObservableCollection here as AvaloniaList signals a Clear as an
  283. // add + remove.
  284. var items = new ObservableCollection<Item>
  285. {
  286. new Item(),
  287. new Item(),
  288. };
  289. var target = new SelectingItemsControl
  290. {
  291. Items = items,
  292. Template = Template(),
  293. };
  294. target.ApplyTemplate();
  295. target.SelectedIndex = 1;
  296. Assert.Equal(items[1], target.SelectedItem);
  297. Assert.Equal(1, target.SelectedIndex);
  298. items.Clear();
  299. Assert.Null(target.SelectedItem);
  300. Assert.Equal(-1, target.SelectedIndex);
  301. }
  302. [Fact]
  303. public void Raising_IsSelectedChanged_On_Item_Should_Update_Selection()
  304. {
  305. var items = new[]
  306. {
  307. new Item(),
  308. new Item(),
  309. };
  310. var target = new SelectingItemsControl
  311. {
  312. Items = items,
  313. Template = Template(),
  314. };
  315. target.ApplyTemplate();
  316. target.Presenter.ApplyTemplate();
  317. target.SelectedItem = items[1];
  318. Assert.False(items[0].IsSelected);
  319. Assert.True(items[1].IsSelected);
  320. items[0].IsSelected = true;
  321. items[0].RaiseEvent(new RoutedEventArgs(SelectingItemsControl.IsSelectedChangedEvent));
  322. Assert.Equal(0, target.SelectedIndex);
  323. Assert.Equal(items[0], target.SelectedItem);
  324. Assert.True(items[0].IsSelected);
  325. Assert.False(items[1].IsSelected);
  326. }
  327. [Fact]
  328. public void Clearing_IsSelected_And_Raising_IsSelectedChanged_On_Item_Should_Update_Selection()
  329. {
  330. var items = new[]
  331. {
  332. new Item(),
  333. new Item(),
  334. };
  335. var target = new SelectingItemsControl
  336. {
  337. Items = items,
  338. Template = Template(),
  339. };
  340. target.ApplyTemplate();
  341. target.Presenter.ApplyTemplate();
  342. target.SelectedItem = items[1];
  343. Assert.False(items[0].IsSelected);
  344. Assert.True(items[1].IsSelected);
  345. items[1].IsSelected = false;
  346. items[1].RaiseEvent(new RoutedEventArgs(SelectingItemsControl.IsSelectedChangedEvent));
  347. Assert.Equal(-1, target.SelectedIndex);
  348. Assert.Null(target.SelectedItem);
  349. }
  350. [Fact]
  351. public void Raising_IsSelectedChanged_On_Someone_Elses_Item_Should_Not_Update_Selection()
  352. {
  353. var items = new[]
  354. {
  355. new Item(),
  356. new Item(),
  357. };
  358. var target = new SelectingItemsControl
  359. {
  360. Items = items,
  361. Template = Template(),
  362. };
  363. target.ApplyTemplate();
  364. target.SelectedItem = items[1];
  365. var notChild = new Item
  366. {
  367. IsSelected = true,
  368. };
  369. target.RaiseEvent(new RoutedEventArgs
  370. {
  371. RoutedEvent = SelectingItemsControl.IsSelectedChangedEvent,
  372. Source = notChild,
  373. });
  374. Assert.Equal(target.SelectedItem, items[1]);
  375. }
  376. [Fact]
  377. public void Setting_SelectedIndex_Should_Raise_SelectionChanged_Event()
  378. {
  379. var items = new[]
  380. {
  381. new Item(),
  382. new Item(),
  383. };
  384. var target = new SelectingItemsControl
  385. {
  386. Items = items,
  387. Template = Template(),
  388. };
  389. var called = false;
  390. target.SelectionChanged += (s, e) =>
  391. {
  392. Assert.Same(items[1], e.AddedItems.Cast<object>().Single());
  393. Assert.Empty(e.RemovedItems);
  394. called = true;
  395. };
  396. target.SelectedIndex = 1;
  397. Assert.True(called);
  398. }
  399. [Fact]
  400. public void Clearing_SelectedIndex_Should_Raise_SelectionChanged_Event()
  401. {
  402. var items = new[]
  403. {
  404. new Item(),
  405. new Item(),
  406. };
  407. var target = new SelectingItemsControl
  408. {
  409. Items = items,
  410. Template = Template(),
  411. SelectedIndex = 1,
  412. };
  413. var called = false;
  414. target.SelectionChanged += (s, e) =>
  415. {
  416. Assert.Same(items[1], e.RemovedItems.Cast<object>().Single());
  417. Assert.Empty(e.AddedItems);
  418. called = true;
  419. };
  420. target.ApplyTemplate();
  421. target.Presenter.ApplyTemplate();
  422. target.SelectedIndex = -1;
  423. Assert.True(called);
  424. }
  425. [Fact]
  426. public void Order_Of_Setting_Items_And_SelectedIndex_During_Initialization_Should_Not_Matter()
  427. {
  428. var items = new[] { "Foo", "Bar" };
  429. var target = new SelectingItemsControl();
  430. ((ISupportInitialize)target).BeginInit();
  431. target.SelectedIndex = 1;
  432. target.Items = items;
  433. ((ISupportInitialize)target).EndInit();
  434. Assert.Equal(1, target.SelectedIndex);
  435. Assert.Equal("Bar", target.SelectedItem);
  436. }
  437. [Fact]
  438. public void Order_Of_Setting_Items_And_SelectedItem_During_Initialization_Should_Not_Matter()
  439. {
  440. var items = new[] { "Foo", "Bar" };
  441. var target = new SelectingItemsControl();
  442. ((ISupportInitialize)target).BeginInit();
  443. target.SelectedItem = "Bar";
  444. target.Items = items;
  445. ((ISupportInitialize)target).EndInit();
  446. Assert.Equal(1, target.SelectedIndex);
  447. Assert.Equal("Bar", target.SelectedItem);
  448. }
  449. [Fact]
  450. public void Changing_DataContext_Should_Not_Clear_Nested_ViewModel_SelectedItem()
  451. {
  452. var items = new[]
  453. {
  454. new Item(),
  455. new Item(),
  456. };
  457. var vm = new MasterViewModel
  458. {
  459. Child = new ChildViewModel
  460. {
  461. Items = items,
  462. SelectedItem = items[1],
  463. }
  464. };
  465. var target = new SelectingItemsControl { DataContext = vm };
  466. var itemsBinding = new Binding("Child.Items");
  467. var selectedBinding = new Binding("Child.SelectedItem");
  468. target.Bind(SelectingItemsControl.ItemsProperty, itemsBinding);
  469. target.Bind(SelectingItemsControl.SelectedItemProperty, selectedBinding);
  470. Assert.Equal(1, target.SelectedIndex);
  471. Assert.Same(vm.Child.SelectedItem, target.SelectedItem);
  472. items = new[]
  473. {
  474. new Item { Value = "Item1" },
  475. new Item { Value = "Item2" },
  476. new Item { Value = "Item3" },
  477. };
  478. vm = new MasterViewModel
  479. {
  480. Child = new ChildViewModel
  481. {
  482. Items = items,
  483. SelectedItem = items[2],
  484. }
  485. };
  486. target.DataContext = vm;
  487. Assert.Equal(2, target.SelectedIndex);
  488. Assert.Same(vm.Child.SelectedItem, target.SelectedItem);
  489. }
  490. [Fact]
  491. public void Nested_ListBox_Does_Not_Change_Parent_SelectedIndex()
  492. {
  493. SelectingItemsControl nested;
  494. var root = new SelectingItemsControl
  495. {
  496. Template = Template(),
  497. Items = new IControl[]
  498. {
  499. new Border(),
  500. nested = new ListBox
  501. {
  502. Template = Template(),
  503. Items = new[] { "foo", "bar" },
  504. SelectedIndex = 1,
  505. }
  506. },
  507. SelectedIndex = 0,
  508. };
  509. root.ApplyTemplate();
  510. root.Presenter.ApplyTemplate();
  511. nested.ApplyTemplate();
  512. nested.Presenter.ApplyTemplate();
  513. Assert.Equal(0, root.SelectedIndex);
  514. Assert.Equal(1, nested.SelectedIndex);
  515. nested.SelectedIndex = 0;
  516. Assert.Equal(0, root.SelectedIndex);
  517. }
  518. [Fact]
  519. public void Setting_SelectedItem_With_Pointer_Should_Set_TabOnceActiveElement()
  520. {
  521. var target = new ListBox
  522. {
  523. Template = Template(),
  524. Items = new[] { "Foo", "Bar", "Baz " },
  525. };
  526. target.ApplyTemplate();
  527. target.Presenter.ApplyTemplate();
  528. target.Presenter.Panel.Children[1].RaiseEvent(new PointerPressedEventArgs
  529. {
  530. RoutedEvent = InputElement.PointerPressedEvent,
  531. MouseButton = MouseButton.Left,
  532. });
  533. var panel = target.Presenter.Panel;
  534. Assert.Equal(
  535. KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel),
  536. panel.Children[1]);
  537. }
  538. [Fact]
  539. public void Removing_SelectedItem_Should_Clear_TabOnceActiveElement()
  540. {
  541. var items = new ObservableCollection<string>(new[] { "Foo", "Bar", "Baz " });
  542. var target = new ListBox
  543. {
  544. Template = Template(),
  545. Items = items,
  546. };
  547. target.ApplyTemplate();
  548. target.Presenter.ApplyTemplate();
  549. target.Presenter.Panel.Children[1].RaiseEvent(new PointerPressedEventArgs
  550. {
  551. RoutedEvent = InputElement.PointerPressedEvent,
  552. MouseButton = MouseButton.Left,
  553. });
  554. items.RemoveAt(1);
  555. var panel = target.Presenter.Panel;
  556. Assert.Null(KeyboardNavigation.GetTabOnceActiveElement((InputElement)panel));
  557. }
  558. [Fact]
  559. public void Resetting_Items_Collection_Should_Retain_Selection()
  560. {
  561. var itemsMock = new Mock<List<string>>();
  562. var itemsMockAsINCC = itemsMock.As<INotifyCollectionChanged>();
  563. itemsMock.Object.AddRange(new[] { "Foo", "Bar", "Baz" });
  564. var target = new SelectingItemsControl
  565. {
  566. Items = itemsMock.Object
  567. };
  568. target.SelectedIndex = 1;
  569. itemsMockAsINCC.Raise(e => e.CollectionChanged += null, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
  570. Assert.True(target.SelectedIndex == 1);
  571. }
  572. [Fact]
  573. public void Binding_With_DelayedBinding_And_Initialization_Where_DataContext_Is_Root_Works()
  574. {
  575. // Test for #1932.
  576. var root = new RootWithItems();
  577. root.BeginInit();
  578. root.DataContext = root;
  579. var target = new ListBox();
  580. target.BeginInit();
  581. root.Child = target;
  582. DelayedBinding.Add(target, ItemsControl.ItemsProperty, new Binding(nameof(RootWithItems.Items)));
  583. DelayedBinding.Add(target, ListBox.SelectedItemProperty, new Binding(nameof(RootWithItems.Selected)));
  584. target.EndInit();
  585. root.EndInit();
  586. Assert.Equal("b", target.SelectedItem);
  587. }
  588. private FuncControlTemplate Template()
  589. {
  590. return new FuncControlTemplate<SelectingItemsControl>(control =>
  591. new ItemsPresenter
  592. {
  593. Name = "itemsPresenter",
  594. [~ItemsPresenter.ItemsProperty] = control[~ItemsControl.ItemsProperty],
  595. [~ItemsPresenter.ItemsPanelProperty] = control[~ItemsControl.ItemsPanelProperty],
  596. });
  597. }
  598. private class Item : Control, ISelectable
  599. {
  600. public string Value { get; set; }
  601. public bool IsSelected { get; set; }
  602. }
  603. private class MasterViewModel : NotifyingBase
  604. {
  605. private ChildViewModel _child;
  606. public ChildViewModel Child
  607. {
  608. get { return _child; }
  609. set
  610. {
  611. _child = value;
  612. RaisePropertyChanged();
  613. }
  614. }
  615. }
  616. private class ChildViewModel : NotifyingBase
  617. {
  618. public IList<Item> Items { get; set; }
  619. public Item SelectedItem { get; set; }
  620. }
  621. private class RootWithItems : TestRoot
  622. {
  623. public List<string> Items { get; set; } = new List<string>() { "a", "b", "c", "d", "e" };
  624. public string Selected { get; set; } = "b";
  625. }
  626. }
  627. }