ListBoxTests_Single.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. using System.Collections.Generic;
  2. using System.ComponentModel;
  3. using System.Linq;
  4. using Avalonia.Controls.Presenters;
  5. using Avalonia.Controls.Primitives;
  6. using Avalonia.Controls.Templates;
  7. using Avalonia.Data;
  8. using Avalonia.Input;
  9. using Avalonia.Input.GestureRecognizers;
  10. using Avalonia.Input.Platform;
  11. using Avalonia.LogicalTree;
  12. using Avalonia.Styling;
  13. using Avalonia.UnitTests;
  14. using Avalonia.VisualTree;
  15. using Moq;
  16. using Xunit;
  17. namespace Avalonia.Controls.UnitTests
  18. {
  19. public class ListBoxTests_Single : ScopedTestBase
  20. {
  21. MouseTestHelper _mouse = new MouseTestHelper();
  22. MouseTestHelper _pen = new MouseTestHelper(PointerType.Pen);
  23. [Fact]
  24. public void Focusing_Item_With_Tab_Should_Not_Select_It()
  25. {
  26. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  27. {
  28. var target = new ListBox
  29. {
  30. Template = new FuncControlTemplate(CreateListBoxTemplate),
  31. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  32. };
  33. Prepare(target);
  34. target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
  35. {
  36. NavigationMethod = NavigationMethod.Tab,
  37. });
  38. Assert.Equal(-1, target.SelectedIndex);
  39. }
  40. }
  41. [Fact]
  42. public void Pressing_Space_On_Focused_Item_With_Ctrl_Pressed_Should_Select_It()
  43. {
  44. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  45. {
  46. var target = new ListBox
  47. {
  48. Template = new FuncControlTemplate(CreateListBoxTemplate),
  49. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  50. };
  51. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
  52. Prepare(target);
  53. target.Presenter.Panel.Children[0].RaiseEvent(new GotFocusEventArgs
  54. {
  55. NavigationMethod = NavigationMethod.Directional,
  56. KeyModifiers = KeyModifiers.Control
  57. });
  58. target.Presenter.Panel.Children[0].RaiseEvent(new KeyEventArgs
  59. {
  60. RoutedEvent = InputElement.KeyDownEvent,
  61. Key = Key.Space,
  62. KeyModifiers = KeyModifiers.Control
  63. });
  64. Assert.Equal(0, target.SelectedIndex);
  65. }
  66. }
  67. [Fact]
  68. public void Clicking_Item_Should_Select_It()
  69. {
  70. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  71. {
  72. var target = new ListBox
  73. {
  74. Template = new FuncControlTemplate(CreateListBoxTemplate),
  75. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  76. };
  77. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
  78. Prepare(target);
  79. _mouse.Click(target.Presenter.Panel.Children[0]);
  80. Assert.Equal(0, target.SelectedIndex);
  81. }
  82. }
  83. [Fact]
  84. public void Pen_Right_Press_Item_Should_Select_It()
  85. {
  86. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  87. {
  88. var target = new ListBox
  89. {
  90. Template = new FuncControlTemplate(CreateListBoxTemplate),
  91. ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
  92. ItemsSource = new[] { "Foo", "Bar", "Baz " }
  93. };
  94. Prepare(target);
  95. _pen.Down(target.Presenter.Panel.Children[0], MouseButton.Right);
  96. Assert.Equal(0, target.SelectedIndex);
  97. }
  98. }
  99. [Fact]
  100. public void Pen_Left_Press_Item_Should_Not_Select_It()
  101. {
  102. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  103. {
  104. var target = new ListBox
  105. {
  106. Template = new FuncControlTemplate(CreateListBoxTemplate),
  107. ItemTemplate = new FuncDataTemplate<string>((x, _) => new TextBlock { Width = 20, Height = 10 }),
  108. ItemsSource = new[] { "Foo", "Bar", "Baz " }
  109. };
  110. Prepare(target);
  111. _pen.Down(target.Presenter.Panel.Children[0]);
  112. Assert.Equal(-1, target.SelectedIndex);
  113. }
  114. }
  115. [Theory]
  116. [InlineData(PointerType.Mouse)]
  117. [InlineData(PointerType.Pen)]
  118. public void Pointer_Right_Click_Should_Select_Item_And_Open_Context(PointerType type)
  119. {
  120. using (UnitTestApplication.Start(TestServices.StyledWindow))
  121. {
  122. var target = new ListBox
  123. {
  124. Template = new FuncControlTemplate(CreateListBoxTemplate),
  125. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  126. ItemTemplate = new FuncDataTemplate<string>((x, _) => new Border { Height = 10 })
  127. };
  128. target.GestureRecognizers.Add(new ScrollGestureRecognizer()
  129. {
  130. CanVerticallyScroll = true, ScrollStartDistance = 50
  131. });
  132. Prepare(target);
  133. var contextRaised = false;
  134. target.AddHandler(Control.ContextRequestedEvent, (sender, args) =>
  135. {
  136. contextRaised = true;
  137. args.Handled = true;
  138. });
  139. var pointer = type == PointerType.Mouse ? _mouse : _pen;
  140. pointer.Click(target.Presenter.Panel.Children[0], MouseButton.Right, position: new Point(5, 5));
  141. Assert.True(contextRaised);
  142. Assert.Equal(0, target.SelectedIndex);
  143. }
  144. }
  145. [Fact]
  146. public void Clicking_Selected_Item_Should_Not_Deselect_It()
  147. {
  148. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  149. {
  150. var target = new ListBox
  151. {
  152. Template = new FuncControlTemplate(CreateListBoxTemplate),
  153. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  154. };
  155. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
  156. Prepare(target);
  157. target.SelectedIndex = 0;
  158. _mouse.Click(target.Presenter.Panel.Children[0]);
  159. Assert.Equal(0, target.SelectedIndex);
  160. }
  161. }
  162. [Fact]
  163. public void Clicking_Item_Should_Select_It_When_SelectionMode_Toggle()
  164. {
  165. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  166. {
  167. var target = new ListBox
  168. {
  169. Template = new FuncControlTemplate(CreateListBoxTemplate),
  170. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  171. SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
  172. };
  173. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
  174. Prepare(target);
  175. _mouse.Click(target.Presenter.Panel.Children[0]);
  176. Assert.Equal(0, target.SelectedIndex);
  177. }
  178. }
  179. [Fact]
  180. public void Clicking_Selected_Item_Should_Deselect_It_When_SelectionMode_Toggle()
  181. {
  182. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  183. {
  184. var target = new ListBox
  185. {
  186. Template = new FuncControlTemplate(CreateListBoxTemplate),
  187. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  188. SelectionMode = SelectionMode.Toggle,
  189. };
  190. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
  191. Prepare(target);
  192. target.SelectedIndex = 0;
  193. _mouse.Click(target.Presenter.Panel.Children[0]);
  194. Assert.Equal(-1, target.SelectedIndex);
  195. }
  196. }
  197. [Fact]
  198. public void Clicking_Selected_Item_Should_Not_Deselect_It_When_SelectionMode_ToggleAlwaysSelected()
  199. {
  200. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  201. {
  202. var target = new ListBox
  203. {
  204. Template = new FuncControlTemplate(CreateListBoxTemplate),
  205. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  206. SelectionMode = SelectionMode.Toggle | SelectionMode.AlwaysSelected,
  207. };
  208. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
  209. Prepare(target);
  210. target.SelectedIndex = 0;
  211. _mouse.Click(target.Presenter.Panel.Children[0]);
  212. Assert.Equal(0, target.SelectedIndex);
  213. }
  214. }
  215. [Fact]
  216. public void Clicking_Another_Item_Should_Select_It_When_SelectionMode_Toggle()
  217. {
  218. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  219. {
  220. var target = new ListBox
  221. {
  222. Template = new FuncControlTemplate(CreateListBoxTemplate),
  223. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  224. SelectionMode = SelectionMode.Single | SelectionMode.Toggle,
  225. };
  226. AvaloniaLocator.CurrentMutable.Bind<PlatformHotkeyConfiguration>().ToConstant(new PlatformHotkeyConfiguration());
  227. Prepare(target);
  228. target.SelectedIndex = 1;
  229. _mouse.Click(target.Presenter.Panel.Children[0]);
  230. Assert.Equal(0, target.SelectedIndex);
  231. }
  232. }
  233. [Fact]
  234. public void Setting_Item_IsSelected_Sets_ListBox_Selection()
  235. {
  236. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  237. {
  238. var target = new ListBox
  239. {
  240. Template = new FuncControlTemplate(CreateListBoxTemplate),
  241. ItemsSource = new[] { "Foo", "Bar", "Baz " },
  242. };
  243. Prepare(target);
  244. ((ListBoxItem)target.GetLogicalChildren().ElementAt(1)).IsSelected = true;
  245. Assert.Equal("Bar", target.SelectedItem);
  246. Assert.Equal(1, target.SelectedIndex);
  247. }
  248. }
  249. [Fact]
  250. public void SelectedItem_Should_Not_Cause_StackOverflow()
  251. {
  252. using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface))
  253. {
  254. var viewModel = new TestStackOverflowViewModel() { Items = new List<string> { "foo", "bar", "baz" } };
  255. var target = new ListBox
  256. {
  257. Template = new FuncControlTemplate(CreateListBoxTemplate),
  258. DataContext = viewModel,
  259. ItemsSource = viewModel.Items
  260. };
  261. target.Bind(ListBox.SelectedItemProperty,
  262. new Binding("SelectedItem") { Mode = BindingMode.TwoWay });
  263. Assert.Equal(0, viewModel.SetterInvokedCount);
  264. // In Issue #855, a Stackoverflow occurred here.
  265. target.SelectedItem = viewModel.Items[2];
  266. Assert.Equal(viewModel.Items[1], target.SelectedItem);
  267. Assert.Equal(1, viewModel.SetterInvokedCount);
  268. }
  269. }
  270. private class TestStackOverflowViewModel : INotifyPropertyChanged
  271. {
  272. public List<string> Items { get; set; }
  273. public int SetterInvokedCount { get; private set; }
  274. public const int MaxInvokedCount = 1000;
  275. private string _selectedItem;
  276. public event PropertyChangedEventHandler PropertyChanged;
  277. public string SelectedItem
  278. {
  279. get { return _selectedItem; }
  280. set
  281. {
  282. if (_selectedItem != value)
  283. {
  284. SetterInvokedCount++;
  285. int index = Items.IndexOf(value);
  286. if (MaxInvokedCount > SetterInvokedCount && index > 0)
  287. {
  288. _selectedItem = Items[index - 1];
  289. }
  290. else
  291. {
  292. _selectedItem = value;
  293. }
  294. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
  295. }
  296. }
  297. }
  298. }
  299. private Control CreateListBoxTemplate(TemplatedControl parent, INameScope scope)
  300. {
  301. return new ScrollViewer
  302. {
  303. Template = new FuncControlTemplate(CreateScrollViewerTemplate),
  304. Content = new ItemsPresenter
  305. {
  306. Name = "PART_ItemsPresenter",
  307. }.RegisterInNameScope(scope)
  308. };
  309. }
  310. private Control CreateScrollViewerTemplate(TemplatedControl parent, INameScope scope)
  311. {
  312. return new ScrollContentPresenter
  313. {
  314. Name = "PART_ContentPresenter",
  315. [~ContentPresenter.ContentProperty] =
  316. parent.GetObservable(ContentControl.ContentProperty).ToBinding(),
  317. }.RegisterInNameScope(scope);
  318. }
  319. private static void Prepare(ListBox target)
  320. {
  321. target.Width = target.Height = 100;
  322. var root = new TestRoot(target);
  323. root.LayoutManager.ExecuteInitialLayoutPass();
  324. }
  325. }
  326. }