ItemsPresenterTests_Virtualization_Simple.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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.Linq;
  6. using Avalonia.Controls.Generators;
  7. using Avalonia.Controls.Presenters;
  8. using Avalonia.Controls.Primitives;
  9. using Avalonia.Controls.Templates;
  10. using Xunit;
  11. namespace Avalonia.Controls.UnitTests.Presenters
  12. {
  13. public class ItemsPresenterTests_Virtualization_Simple
  14. {
  15. [Fact]
  16. public void Should_Return_Items_Count_For_Extent_Vertical()
  17. {
  18. var target = CreateTarget();
  19. target.ApplyTemplate();
  20. Assert.Equal(new Size(0, 20), ((ILogicalScrollable)target).Extent);
  21. }
  22. [Fact]
  23. public void Should_Return_Items_Count_For_Extent_Horizontal()
  24. {
  25. var target = CreateTarget(orientation: Orientation.Horizontal);
  26. target.ApplyTemplate();
  27. Assert.Equal(new Size(20, 0), ((ILogicalScrollable)target).Extent);
  28. }
  29. [Fact]
  30. public void Should_Have_Number_Of_Visible_Items_As_Viewport_Vertical()
  31. {
  32. var target = CreateTarget();
  33. target.ApplyTemplate();
  34. target.Measure(new Size(100, 100));
  35. target.Arrange(new Rect(0, 0, 100, 100));
  36. Assert.Equal(new Size(0, 10), ((ILogicalScrollable)target).Viewport);
  37. }
  38. [Fact]
  39. public void Should_Have_Number_Of_Visible_Items_As_Viewport_Horizontal()
  40. {
  41. var target = CreateTarget(orientation: Orientation.Horizontal);
  42. target.ApplyTemplate();
  43. target.Measure(new Size(100, 100));
  44. target.Arrange(new Rect(0, 0, 100, 100));
  45. Assert.Equal(new Size(10, 0), ((ILogicalScrollable)target).Viewport);
  46. }
  47. [Fact]
  48. public void Should_Remove_Items_When_Control_Is_Shrank()
  49. {
  50. var target = CreateTarget();
  51. var items = (IList<string>)target.Items;
  52. target.ApplyTemplate();
  53. target.Measure(new Size(100, 100));
  54. target.Arrange(new Rect(0, 0, 100, 100));
  55. Assert.Equal(10, target.Panel.Children.Count);
  56. target.Arrange(new Rect(0, 0, 100, 80));
  57. Assert.Equal(8, target.Panel.Children.Count);
  58. }
  59. [Fact]
  60. public void Should_Add_New_Items_At_Top_When_Control_Is_Scrolled_To_Bottom_And_Enlarged()
  61. {
  62. var target = CreateTarget();
  63. var items = (IList<string>)target.Items;
  64. target.ApplyTemplate();
  65. target.Measure(new Size(100, 100));
  66. target.Arrange(new Rect(0, 0, 100, 100));
  67. Assert.Equal(10, target.Panel.Children.Count);
  68. ((IScrollable)target).Offset = new Vector(0, 10);
  69. target.Arrange(new Rect(0, 0, 100, 120));
  70. Assert.Equal(12, target.Panel.Children.Count);
  71. for (var i = 0; i < target.Panel.Children.Count; ++i)
  72. {
  73. Assert.Equal(items[i + 8], target.Panel.Children[i].DataContext);
  74. }
  75. }
  76. [Fact]
  77. public void Should_Update_Containers_When_Items_Changes()
  78. {
  79. var target = CreateTarget();
  80. target.ApplyTemplate();
  81. target.Measure(new Size(100, 100));
  82. target.Arrange(new Rect(0, 0, 100, 100));
  83. target.Items = new[] { "foo", "bar", "baz" };
  84. Assert.Equal(3, target.Panel.Children.Count);
  85. }
  86. [Fact]
  87. public void Should_Decrease_The_Viewport_Size_By_One_If_There_Is_A_Partial_Item()
  88. {
  89. var target = CreateTarget();
  90. target.ApplyTemplate();
  91. target.Measure(new Size(100, 95));
  92. target.Arrange(new Rect(0, 0, 100, 95));
  93. Assert.Equal(new Size(0, 9), ((ILogicalScrollable)target).Viewport);
  94. }
  95. [Fact]
  96. public void Moving_To_And_From_The_End_With_Partial_Item_Should_Set_Panel_PixelOffset()
  97. {
  98. var target = CreateTarget(itemCount: 20);
  99. target.ApplyTemplate();
  100. target.Measure(new Size(100, 95));
  101. target.Arrange(new Rect(0, 0, 100, 95));
  102. ((ILogicalScrollable)target).Offset = new Vector(0, 11);
  103. var minIndex = target.ItemContainerGenerator.Containers.Min(x => x.Index);
  104. Assert.Equal(new Vector(0, 11), ((ILogicalScrollable)target).Offset);
  105. Assert.Equal(10, minIndex);
  106. Assert.Equal(5, ((IVirtualizingPanel)target.Panel).PixelOffset);
  107. ((ILogicalScrollable)target).Offset = new Vector(0, 10);
  108. minIndex = target.ItemContainerGenerator.Containers.Min(x => x.Index);
  109. Assert.Equal(new Vector(0, 10), ((ILogicalScrollable)target).Offset);
  110. Assert.Equal(10, minIndex);
  111. Assert.Equal(0, ((IVirtualizingPanel)target.Panel).PixelOffset);
  112. ((ILogicalScrollable)target).Offset = new Vector(0, 11);
  113. minIndex = target.ItemContainerGenerator.Containers.Min(x => x.Index);
  114. Assert.Equal(new Vector(0, 11), ((ILogicalScrollable)target).Offset);
  115. Assert.Equal(10, minIndex);
  116. Assert.Equal(5, ((IVirtualizingPanel)target.Panel).PixelOffset);
  117. }
  118. [Fact]
  119. public void Inserting_Items_Should_Update_Containers()
  120. {
  121. var target = CreateTarget(itemCount: 20);
  122. target.ApplyTemplate();
  123. target.Measure(new Size(100, 95));
  124. target.Arrange(new Rect(0, 0, 100, 95));
  125. ((ILogicalScrollable)target).Offset = new Vector(0, 5);
  126. var expected = Enumerable.Range(5, 10).Select(x => $"Item {x}").ToList();
  127. var items = (ObservableCollection<string>)target.Items;
  128. Assert.Equal(
  129. expected,
  130. target.Panel.Children.Select(x => x.DataContext));
  131. items.Insert(6, "Inserted");
  132. expected.Insert(1, "Inserted");
  133. expected.RemoveAt(expected.Count - 1);
  134. var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
  135. Assert.Equal(expected, actual);
  136. }
  137. public class WithContainers
  138. {
  139. [Fact]
  140. public void Scrolling_Less_Than_A_Page_Should_Move_Recycled_Items()
  141. {
  142. var target = CreateTarget();
  143. var items = (IList<string>)target.Items;
  144. target.ApplyTemplate();
  145. target.Measure(new Size(100, 100));
  146. target.Arrange(new Rect(0, 0, 100, 100));
  147. var containers = target.Panel.Children.ToList();
  148. var scroller = (ScrollContentPresenter)target.Parent;
  149. scroller.Offset = new Vector(0, 5);
  150. var scrolledContainers = containers
  151. .Skip(5)
  152. .Take(5)
  153. .Concat(containers.Take(5)).ToList();
  154. Assert.Equal(new Vector(0, 5), ((ILogicalScrollable)target).Offset);
  155. Assert.Equal(scrolledContainers, target.Panel.Children);
  156. for (var i = 0; i < target.Panel.Children.Count; ++i)
  157. {
  158. Assert.Equal(items[i + 5], target.Panel.Children[i].DataContext);
  159. }
  160. scroller.Offset = new Vector(0, 0);
  161. Assert.Equal(new Vector(0, 0), ((ILogicalScrollable)target).Offset);
  162. Assert.Equal(containers, target.Panel.Children);
  163. var dcs = target.Panel.Children.Select(x => x.DataContext).ToList();
  164. for (var i = 0; i < target.Panel.Children.Count; ++i)
  165. {
  166. Assert.Equal(items[i], target.Panel.Children[i].DataContext);
  167. }
  168. }
  169. [Fact]
  170. public void Scrolling_More_Than_A_Page_Should_Recycle_Items()
  171. {
  172. var target = CreateTarget(itemCount: 50);
  173. var items = (IList<string>)target.Items;
  174. target.ApplyTemplate();
  175. target.Measure(new Size(100, 100));
  176. target.Arrange(new Rect(0, 0, 100, 100));
  177. var containers = target.Panel.Children.ToList();
  178. var scroller = (ScrollContentPresenter)target.Parent;
  179. scroller.Offset = new Vector(0, 20);
  180. Assert.Equal(new Vector(0, 20), ((ILogicalScrollable)target).Offset);
  181. Assert.Equal(containers, target.Panel.Children);
  182. for (var i = 0; i < target.Panel.Children.Count; ++i)
  183. {
  184. Assert.Equal(items[i + 20], target.Panel.Children[i].DataContext);
  185. }
  186. scroller.Offset = new Vector(0, 0);
  187. Assert.Equal(new Vector(0, 0), ((ILogicalScrollable)target).Offset);
  188. Assert.Equal(containers, target.Panel.Children);
  189. for (var i = 0; i < target.Panel.Children.Count; ++i)
  190. {
  191. Assert.Equal(items[i], target.Panel.Children[i].DataContext);
  192. }
  193. }
  194. }
  195. private static ItemsPresenter CreateTarget(
  196. Orientation orientation = Orientation.Vertical,
  197. bool useContainers = true,
  198. int itemCount = 20)
  199. {
  200. ItemsPresenter result;
  201. var items = new ObservableCollection<string>(
  202. Enumerable.Range(0, itemCount).Select(x => $"Item {x}"));
  203. var scroller = new ScrollContentPresenter
  204. {
  205. Content = result = new TestItemsPresenter(useContainers)
  206. {
  207. Items = items,
  208. ItemsPanel = VirtualizingPanelTemplate(orientation),
  209. ItemTemplate = ItemTemplate(),
  210. VirtualizationMode = ItemVirtualizationMode.Simple,
  211. }
  212. };
  213. scroller.UpdateChild();
  214. return result;
  215. }
  216. private static IDataTemplate ItemTemplate()
  217. {
  218. return new FuncDataTemplate<string>(x => new Canvas
  219. {
  220. Width = 10,
  221. Height = 10,
  222. });
  223. }
  224. private static ITemplate<IPanel> VirtualizingPanelTemplate(
  225. Orientation orientation = Orientation.Vertical)
  226. {
  227. return new FuncTemplate<IPanel>(() => new VirtualizingStackPanel
  228. {
  229. Orientation = orientation,
  230. });
  231. }
  232. private class TestItemsPresenter : ItemsPresenter
  233. {
  234. private bool _useContainers;
  235. public TestItemsPresenter(bool useContainers)
  236. {
  237. _useContainers = useContainers;
  238. }
  239. protected override IItemContainerGenerator CreateItemContainerGenerator()
  240. {
  241. return _useContainers ?
  242. new ItemContainerGenerator<TestContainer>(this, TestContainer.ContentProperty, null) :
  243. new ItemContainerGenerator(this);
  244. }
  245. }
  246. private class TestContainer : ContentControl
  247. {
  248. public TestContainer()
  249. {
  250. Width = 10;
  251. Height = 10;
  252. }
  253. }
  254. }
  255. }