CarouselTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. using System;
  2. using System.Collections.ObjectModel;
  3. using System.Linq;
  4. using System.Reactive.Subjects;
  5. using Avalonia.Controls.Presenters;
  6. using Avalonia.Controls.Primitives;
  7. using Avalonia.Controls.Templates;
  8. using Avalonia.Data;
  9. using Avalonia.Layout;
  10. using Avalonia.LogicalTree;
  11. using Avalonia.UnitTests;
  12. using Avalonia.VisualTree;
  13. using Xunit;
  14. namespace Avalonia.Controls.UnitTests
  15. {
  16. public class CarouselTests
  17. {
  18. [Fact]
  19. public void First_Item_Should_Be_Selected_By_Default()
  20. {
  21. using var app = Start();
  22. var target = new Carousel
  23. {
  24. Template = CarouselTemplate(),
  25. ItemsSource = new[]
  26. {
  27. "Foo",
  28. "Bar"
  29. }
  30. };
  31. Prepare(target);
  32. Assert.Equal(0, target.SelectedIndex);
  33. Assert.Equal("Foo", target.SelectedItem);
  34. }
  35. [Fact]
  36. public void LogicalChild_Should_Be_Selected_Item()
  37. {
  38. using var app = Start();
  39. var target = new Carousel
  40. {
  41. Template = CarouselTemplate(),
  42. ItemsSource = new[]
  43. {
  44. "Foo",
  45. "Bar"
  46. }
  47. };
  48. Prepare(target);
  49. Assert.Single(target.GetRealizedContainers());
  50. var child = GetContainerTextBlock(target.GetRealizedContainers().Single());
  51. Assert.Equal("Foo", child.Text);
  52. }
  53. [Fact]
  54. public void Selected_Item_Changes_To_First_Item_When_Items_Property_Changes()
  55. {
  56. using var app = Start();
  57. var items = new ObservableCollection<string>
  58. {
  59. "Foo",
  60. "Bar",
  61. "FooBar"
  62. };
  63. var target = new Carousel
  64. {
  65. Template = CarouselTemplate(),
  66. ItemsSource = items,
  67. };
  68. Prepare(target);
  69. Assert.Single(target.GetRealizedContainers());
  70. var child = GetContainerTextBlock(target.GetRealizedContainers().Single());
  71. Assert.Equal("Foo", child.Text);
  72. var newItems = items.ToList();
  73. newItems.RemoveAt(0);
  74. Layout(target);
  75. target.ItemsSource = newItems;
  76. Layout(target);
  77. child = GetContainerTextBlock(target.GetRealizedContainers().Single());
  78. Assert.Equal("Bar", child.Text);
  79. }
  80. [Fact]
  81. public void Selected_Item_Changes_To_First_Item_When_Item_Added()
  82. {
  83. using var app = Start();
  84. var items = new ObservableCollection<string>();
  85. var target = new Carousel
  86. {
  87. Template = CarouselTemplate(),
  88. ItemsSource = items,
  89. };
  90. Prepare(target);
  91. Assert.Equal(-1, target.SelectedIndex);
  92. Assert.Empty(target.GetRealizedContainers());
  93. items.Add("Foo");
  94. Layout(target);
  95. Assert.Equal(0, target.SelectedIndex);
  96. Assert.Single(target.GetRealizedContainers());
  97. }
  98. [Fact]
  99. public void Selected_Index_Changes_To_None_When_Items_Assigned_Null()
  100. {
  101. using var app = Start();
  102. var items = new ObservableCollection<string>
  103. {
  104. "Foo",
  105. "Bar",
  106. "FooBar"
  107. };
  108. var target = new Carousel
  109. {
  110. Template = CarouselTemplate(),
  111. ItemsSource = items,
  112. };
  113. Prepare(target);
  114. Assert.Equal(1, target.GetRealizedContainers().Count());
  115. var child = GetContainerTextBlock(target.GetRealizedContainers().First());
  116. Assert.Equal("Foo", child.Text);
  117. target.ItemsSource = null;
  118. Layout(target);
  119. var numChildren = target.GetRealizedContainers().Count();
  120. Assert.Equal(-1, target.SelectedIndex);
  121. }
  122. [Fact]
  123. public void Selected_Index_Is_Maintained_Carousel_Created_With_Non_Zero_SelectedIndex()
  124. {
  125. using var app = Start();
  126. var items = new ObservableCollection<string>
  127. {
  128. "Foo",
  129. "Bar",
  130. "FooBar"
  131. };
  132. var target = new Carousel
  133. {
  134. Template = CarouselTemplate(),
  135. ItemsSource = items,
  136. SelectedIndex = 2
  137. };
  138. Prepare(target);
  139. Assert.Equal("FooBar", target.SelectedItem);
  140. var child = GetContainerTextBlock(target.GetRealizedContainers().LastOrDefault());
  141. Assert.Equal("FooBar", child.Text);
  142. }
  143. [Fact]
  144. public void Selected_Item_Changes_To_Next_First_Item_When_Item_Removed_From_Beggining_Of_List()
  145. {
  146. using var app = Start();
  147. var items = new ObservableCollection<string>
  148. {
  149. "Foo",
  150. "Bar",
  151. "FooBar"
  152. };
  153. var target = new Carousel
  154. {
  155. Template = CarouselTemplate(),
  156. ItemsSource = items,
  157. };
  158. Prepare(target);
  159. var child = GetContainerTextBlock(target.GetRealizedContainers().First());
  160. Assert.Equal("Foo", child.Text);
  161. items.RemoveAt(0);
  162. Layout(target);
  163. child = GetContainerTextBlock(target.GetRealizedContainers().First());
  164. Assert.IsType<TextBlock>(child);
  165. Assert.Equal("Bar", child.Text);
  166. }
  167. [Fact]
  168. public void Selected_Item_Changes_To_First_Item_If_SelectedItem_Is_Removed_From_Middle()
  169. {
  170. using var app = Start();
  171. var items = new ObservableCollection<string>
  172. {
  173. "Foo",
  174. "Bar",
  175. "FooBar"
  176. };
  177. var target = new Carousel
  178. {
  179. Template = CarouselTemplate(),
  180. ItemsSource = items,
  181. };
  182. Prepare(target);
  183. target.SelectedIndex = 1;
  184. items.RemoveAt(1);
  185. Assert.Equal(0, target.SelectedIndex);
  186. Assert.Equal("Foo", target.SelectedItem);
  187. }
  188. [Fact]
  189. public void SelectedItem_Validation()
  190. {
  191. using (UnitTestApplication.Start(TestServices.MockThreadingInterface))
  192. {
  193. var target = new Carousel
  194. {
  195. Template = CarouselTemplate(),
  196. };
  197. Prepare(target);
  198. var exception = new System.InvalidCastException("failed validation");
  199. var textObservable =
  200. new BehaviorSubject<BindingNotification>(new BindingNotification(exception,
  201. BindingErrorType.DataValidationError));
  202. target.Bind(ComboBox.SelectedItemProperty, textObservable);
  203. Assert.True(DataValidationErrors.GetHasErrors(target));
  204. Assert.True(DataValidationErrors.GetErrors(target).SequenceEqual(new[] { exception }));
  205. }
  206. }
  207. [Fact]
  208. public void Can_Move_Forward_Back_Forward()
  209. {
  210. using var app = Start();
  211. var items = new[] { "foo", "bar" };
  212. var target = new Carousel
  213. {
  214. Template = CarouselTemplate(),
  215. ItemsSource = items,
  216. };
  217. Prepare(target);
  218. target.SelectedIndex = 1;
  219. Layout(target);
  220. Assert.Equal(1, target.SelectedIndex);
  221. target.SelectedIndex = 0;
  222. Layout(target);
  223. Assert.Equal(0, target.SelectedIndex);
  224. target.SelectedIndex = 1;
  225. Layout(target);
  226. Assert.Equal(1, target.SelectedIndex);
  227. }
  228. [Fact]
  229. public void Can_Move_Forward_Back_Forward_With_Control_Items()
  230. {
  231. // Issue #11119
  232. using var app = Start();
  233. var items = new[] { new Canvas(), new Canvas() };
  234. var target = new Carousel
  235. {
  236. Template = CarouselTemplate(),
  237. ItemsSource = items,
  238. };
  239. Prepare(target);
  240. target.SelectedIndex = 1;
  241. Layout(target);
  242. Assert.Equal(1, target.SelectedIndex);
  243. target.SelectedIndex = 0;
  244. Layout(target);
  245. Assert.Equal(0, target.SelectedIndex);
  246. target.SelectedIndex = 1;
  247. target.PropertyChanged += (s, e) =>
  248. {
  249. if (e.Property == Carousel.SelectedIndexProperty)
  250. {
  251. }
  252. };
  253. Layout(target);
  254. Assert.Equal(1, target.SelectedIndex);
  255. }
  256. private static IDisposable Start() => UnitTestApplication.Start(TestServices.MockPlatformRenderInterface);
  257. private static void Prepare(Carousel target)
  258. {
  259. var root = new TestRoot(target);
  260. root.LayoutManager.ExecuteInitialLayoutPass();
  261. }
  262. private static void Layout(Carousel target)
  263. {
  264. ((ILayoutRoot)target.GetVisualRoot()).LayoutManager.ExecuteLayoutPass();
  265. }
  266. private static IControlTemplate CarouselTemplate()
  267. {
  268. return new FuncControlTemplate((c, ns) =>
  269. new ScrollViewer
  270. {
  271. Name = "PART_ScrollViewer",
  272. Template = ScrollViewerTemplate(),
  273. HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden,
  274. VerticalScrollBarVisibility = ScrollBarVisibility.Hidden,
  275. Content = new ItemsPresenter
  276. {
  277. Name = "PART_ItemsPresenter",
  278. [~ItemsPresenter.ItemsPanelProperty] = c[~ItemsControl.ItemsPanelProperty],
  279. }.RegisterInNameScope(ns)
  280. }.RegisterInNameScope(ns));
  281. }
  282. private static FuncControlTemplate ScrollViewerTemplate()
  283. {
  284. return new FuncControlTemplate<ScrollViewer>((parent, scope) =>
  285. new Panel
  286. {
  287. Children =
  288. {
  289. new ScrollContentPresenter
  290. {
  291. Name = "PART_ContentPresenter",
  292. }.RegisterInNameScope(scope),
  293. }
  294. });
  295. }
  296. private static TextBlock GetContainerTextBlock(object control)
  297. {
  298. var contentPresenter = Assert.IsType<ContentPresenter>(control);
  299. return Assert.IsType<TextBlock>(contentPresenter.Child);
  300. }
  301. }
  302. }