ListBoxTests_Single.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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.ComponentModel;
  5. using System.Linq;
  6. using Avalonia.Controls.Presenters;
  7. using Avalonia.Controls.Templates;
  8. using Avalonia.Data;
  9. using Avalonia.Input;
  10. using Avalonia.LogicalTree;
  11. using Avalonia.Markup.Xaml.Data;
  12. using Avalonia.Styling;
  13. using Avalonia.VisualTree;
  14. using Xunit;
  15. namespace Avalonia.Controls.UnitTests
  16. {
  17. public class ListBoxTests_Single
  18. {
  19. [Fact]
  20. public void Focusing_Item_With_Tab_Should_Not_Select_It()
  21. {
  22. var target = new ListBox
  23. {
  24. Template = new FuncControlTemplate(CreateListBoxTemplate),
  25. Items = new[] { "Foo", "Bar", "Baz " },
  26. };
  27. ApplyTemplate(target);
  28. target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
  29. {
  30. RoutedEvent = InputElement.GotFocusEvent,
  31. NavigationMethod = NavigationMethod.Tab,
  32. });
  33. Assert.Equal(-1, target.SelectedIndex);
  34. }
  35. [Fact]
  36. public void Focusing_Item_With_Arrow_Key_Should_Select_It()
  37. {
  38. var target = new ListBox
  39. {
  40. Template = new FuncControlTemplate(CreateListBoxTemplate),
  41. Items = new[] { "Foo", "Bar", "Baz " },
  42. };
  43. ApplyTemplate(target);
  44. target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
  45. {
  46. RoutedEvent = InputElement.GotFocusEvent,
  47. NavigationMethod = NavigationMethod.Directional,
  48. });
  49. Assert.Equal(0, target.SelectedIndex);
  50. }
  51. [Fact]
  52. public void Clicking_Item_Should_Select_It()
  53. {
  54. var target = new ListBox
  55. {
  56. Template = new FuncControlTemplate(CreateListBoxTemplate),
  57. Items = new[] { "Foo", "Bar", "Baz " },
  58. };
  59. ApplyTemplate(target);
  60. target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
  61. {
  62. RoutedEvent = InputElement.PointerPressedEvent,
  63. MouseButton = MouseButton.Left,
  64. });
  65. Assert.Equal(0, target.SelectedIndex);
  66. }
  67. [Fact]
  68. public void Clicking_Selected_Item_Should_Not_Deselect_It()
  69. {
  70. var target = new ListBox
  71. {
  72. Template = new FuncControlTemplate(CreateListBoxTemplate),
  73. Items = new[] { "Foo", "Bar", "Baz " },
  74. };
  75. ApplyTemplate(target);
  76. target.SelectedIndex = 0;
  77. target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
  78. {
  79. RoutedEvent = InputElement.PointerPressedEvent,
  80. MouseButton = MouseButton.Left,
  81. });
  82. Assert.Equal(0, target.SelectedIndex);
  83. }
  84. [Fact]
  85. public void Clicking_Item_Should_Select_It_When_SelectionMode_Toggle()
  86. {
  87. var target = new ListBox
  88. {
  89. Template = new FuncControlTemplate(CreateListBoxTemplate),
  90. Items = new[] { "Foo", "Bar", "Baz " },
  91. SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
  92. };
  93. ApplyTemplate(target);
  94. target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
  95. {
  96. RoutedEvent = InputElement.PointerPressedEvent,
  97. MouseButton = MouseButton.Left,
  98. });
  99. Assert.Equal(0, target.SelectedIndex);
  100. }
  101. [Fact]
  102. public void Clicking_Selected_Item_Should_Deselect_It_When_SelectionMode_Toggle()
  103. {
  104. var target = new ListBox
  105. {
  106. Template = new FuncControlTemplate(CreateListBoxTemplate),
  107. Items = new[] { "Foo", "Bar", "Baz " },
  108. SelectionMode = SelectionMode.Toggle,
  109. };
  110. ApplyTemplate(target);
  111. target.SelectedIndex = 0;
  112. target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
  113. {
  114. RoutedEvent = InputElement.PointerPressedEvent,
  115. MouseButton = MouseButton.Left,
  116. });
  117. Assert.Equal(-1, target.SelectedIndex);
  118. }
  119. [Fact]
  120. public void Clicking_Selected_Item_Should_Not_Deselect_It_When_SelectionMode_ToggleAlwaysSelected()
  121. {
  122. var target = new ListBox
  123. {
  124. Template = new FuncControlTemplate(CreateListBoxTemplate),
  125. Items = new[] { "Foo", "Bar", "Baz " },
  126. SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected,
  127. };
  128. ApplyTemplate(target);
  129. target.SelectedIndex = 0;
  130. target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
  131. {
  132. RoutedEvent = InputElement.PointerPressedEvent,
  133. MouseButton = MouseButton.Left,
  134. });
  135. Assert.Equal(0, target.SelectedIndex);
  136. }
  137. [Fact]
  138. public void Clicking_Another_Item_Should_Select_It_When_SelectionMode_Toggle()
  139. {
  140. var target = new ListBox
  141. {
  142. Template = new FuncControlTemplate(CreateListBoxTemplate),
  143. Items = new[] { "Foo", "Bar", "Baz " },
  144. SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
  145. };
  146. ApplyTemplate(target);
  147. target.SelectedIndex = 1;
  148. target.Presenter.Panel.Children[0].RaiseEvent(new PointerPressedEventArgs
  149. {
  150. RoutedEvent = InputElement.PointerPressedEvent,
  151. MouseButton = MouseButton.Left,
  152. });
  153. Assert.Equal(0, target.SelectedIndex);
  154. }
  155. [Fact]
  156. public void Setting_Item_IsSelected_Sets_ListBox_Selection()
  157. {
  158. var target = new ListBox
  159. {
  160. Template = new FuncControlTemplate(CreateListBoxTemplate),
  161. Items = new[] { "Foo", "Bar", "Baz " },
  162. };
  163. ApplyTemplate(target);
  164. ((ListBoxItem)target.GetLogicalChildren().ElementAt(1)).IsSelected = true;
  165. Assert.Equal("Bar", target.SelectedItem);
  166. Assert.Equal(1, target.SelectedIndex);
  167. }
  168. [Fact]
  169. public void SelectedItem_Should_Not_Cause_StackOverflow()
  170. {
  171. var viewModel = new TestStackOverflowViewModel()
  172. {
  173. Items = new List<string> { "foo", "bar", "baz" }
  174. };
  175. var target = new ListBox
  176. {
  177. Template = new FuncControlTemplate(CreateListBoxTemplate),
  178. DataContext = viewModel,
  179. Items = viewModel.Items
  180. };
  181. target.Bind(ListBox.SelectedItemProperty,
  182. new Binding("SelectedItem") { Mode = BindingMode.TwoWay });
  183. Assert.Equal(0, viewModel.SetterInvokedCount);
  184. // In Issue #855, a Stackoverflow occured here.
  185. target.SelectedItem = viewModel.Items[2];
  186. Assert.Equal(viewModel.Items[1], target.SelectedItem);
  187. Assert.Equal(1, viewModel.SetterInvokedCount);
  188. }
  189. private class TestStackOverflowViewModel : INotifyPropertyChanged
  190. {
  191. public List<string> Items { get; set; }
  192. public int SetterInvokedCount { get; private set; }
  193. public const int MaxInvokedCount = 1000;
  194. private string _selectedItem;
  195. public event PropertyChangedEventHandler PropertyChanged;
  196. public string SelectedItem
  197. {
  198. get { return _selectedItem; }
  199. set
  200. {
  201. if (_selectedItem != value)
  202. {
  203. SetterInvokedCount++;
  204. int index = Items.IndexOf(value);
  205. if (MaxInvokedCount > SetterInvokedCount && index > 0)
  206. {
  207. _selectedItem = Items[index - 1];
  208. }
  209. else
  210. {
  211. _selectedItem = value;
  212. }
  213. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
  214. }
  215. }
  216. }
  217. }
  218. private Control CreateListBoxTemplate(ITemplatedControl parent)
  219. {
  220. return new ScrollViewer
  221. {
  222. Template = new FuncControlTemplate(CreateScrollViewerTemplate),
  223. Content = new ItemsPresenter
  224. {
  225. Name = "PART_ItemsPresenter",
  226. [~ItemsPresenter.ItemsProperty] = parent.GetObservable(ItemsControl.ItemsProperty).ToBinding(),
  227. }
  228. };
  229. }
  230. private Control CreateScrollViewerTemplate(ITemplatedControl parent)
  231. {
  232. return new ScrollContentPresenter
  233. {
  234. Name = "PART_ContentPresenter",
  235. [~ContentPresenter.ContentProperty] = parent.GetObservable(ContentControl.ContentProperty).ToBinding(),
  236. };
  237. }
  238. private void ApplyTemplate(ListBox target)
  239. {
  240. // Apply the template to the ListBox itself.
  241. target.ApplyTemplate();
  242. // Then to its inner ScrollViewer.
  243. var scrollViewer = (ScrollViewer)target.GetVisualChildren().Single();
  244. scrollViewer.ApplyTemplate();
  245. // Then make the ScrollViewer create its child.
  246. ((ContentPresenter)scrollViewer.Presenter).UpdateChild();
  247. // Now the ItemsPresenter should be reigstered, so apply its template.
  248. target.Presenter.ApplyTemplate();
  249. }
  250. }
  251. }