SelectingItemsControlTests.cs 22 KB

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