ItemsPresenterTests_Virtualization_Simple.cs 9.4 KB

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