ItemsPresenterTests_Virtualization.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  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 Avalonia.Layout;
  10. using Avalonia.UnitTests;
  11. using Xunit;
  12. namespace Avalonia.Controls.UnitTests.Presenters
  13. {
  14. public class ItemsPresenterTests_Virtualization
  15. {
  16. [Fact]
  17. public void Should_Return_IsLogicalScrollEnabled_False_When_Has_No_Virtualizing_Panel()
  18. {
  19. var target = CreateTarget();
  20. target.ClearValue(ItemsPresenter.ItemsPanelProperty);
  21. target.ApplyTemplate();
  22. Assert.False(((ILogicalScrollable)target).IsLogicalScrollEnabled);
  23. }
  24. [Fact]
  25. public void Should_Return_IsLogicalScrollEnabled_False_When_VirtualizationMode_None()
  26. {
  27. var target = CreateTarget(ItemVirtualizationMode.None);
  28. target.ApplyTemplate();
  29. Assert.False(((ILogicalScrollable)target).IsLogicalScrollEnabled);
  30. }
  31. [Fact]
  32. public void Should_Return_IsLogicalScrollEnabled_False_When_Doesnt_Have_ScrollPresenter_Parent()
  33. {
  34. var target = new ItemsPresenter
  35. {
  36. ItemsPanel = VirtualizingPanelTemplate(),
  37. ItemTemplate = ItemTemplate(),
  38. VirtualizationMode = ItemVirtualizationMode.Simple,
  39. };
  40. target.ApplyTemplate();
  41. Assert.False(((ILogicalScrollable)target).IsLogicalScrollEnabled);
  42. }
  43. [Fact]
  44. public void Should_Return_IsLogicalScrollEnabled_True()
  45. {
  46. var target = CreateTarget();
  47. target.ApplyTemplate();
  48. Assert.True(((ILogicalScrollable)target).IsLogicalScrollEnabled);
  49. }
  50. [Fact]
  51. public void Parent_ScrollContentPresenter_Properties_Should_Be_Set()
  52. {
  53. var target = CreateTarget();
  54. target.ApplyTemplate();
  55. target.Measure(new Size(100, 100));
  56. target.Arrange(new Rect(0, 0, 100, 100));
  57. var scroll = (ScrollContentPresenter)target.Parent;
  58. Assert.Equal(new Size(0, 20), scroll.Extent);
  59. Assert.Equal(new Size(0, 10), scroll.Viewport);
  60. }
  61. [Fact]
  62. public void Should_Fill_Panel_With_Containers()
  63. {
  64. var target = CreateTarget();
  65. target.ApplyTemplate();
  66. target.Measure(new Size(100, 100));
  67. Assert.Equal(10, target.Panel.Children.Count);
  68. target.Arrange(new Rect(0, 0, 100, 100));
  69. Assert.Equal(10, target.Panel.Children.Count);
  70. }
  71. [Fact]
  72. public void Should_Only_Create_Enough_Containers_To_Display_All_Items()
  73. {
  74. var target = CreateTarget(itemCount: 2);
  75. target.ApplyTemplate();
  76. target.Measure(new Size(100, 100));
  77. target.Arrange(new Rect(0, 0, 100, 100));
  78. Assert.Equal(2, target.Panel.Children.Count);
  79. }
  80. [Fact]
  81. public void Should_Expand_To_Fit_Containers_When_Flexible_Size()
  82. {
  83. var target = CreateTarget();
  84. target.ApplyTemplate();
  85. target.Measure(Size.Infinity);
  86. target.Arrange(new Rect(target.DesiredSize));
  87. Assert.Equal(new Size(10, 200), target.DesiredSize);
  88. Assert.Equal(new Size(10, 200), target.Bounds.Size);
  89. Assert.Equal(20, target.Panel.Children.Count);
  90. }
  91. [Fact]
  92. public void Initial_Item_DataContexts_Should_Be_Correct()
  93. {
  94. var target = CreateTarget();
  95. var items = (IList<string>)target.Items;
  96. target.ApplyTemplate();
  97. target.Measure(new Size(100, 100));
  98. target.Arrange(new Rect(0, 0, 100, 100));
  99. for (var i = 0; i < target.Panel.Children.Count; ++i)
  100. {
  101. Assert.Equal(items[i], target.Panel.Children[i].DataContext);
  102. }
  103. }
  104. [Fact]
  105. public void Should_Add_New_Items_When_Control_Is_Enlarged()
  106. {
  107. var target = CreateTarget();
  108. var items = (IList<string>)target.Items;
  109. target.ApplyTemplate();
  110. target.Measure(new Size(100, 100));
  111. target.Arrange(new Rect(0, 0, 100, 100));
  112. Assert.Equal(10, target.Panel.Children.Count);
  113. target.Measure(new Size(120, 120));
  114. target.Arrange(new Rect(0, 0, 100, 120));
  115. Assert.Equal(12, target.Panel.Children.Count);
  116. for (var i = 0; i < target.Panel.Children.Count; ++i)
  117. {
  118. Assert.Equal(items[i], target.Panel.Children[i].DataContext);
  119. }
  120. }
  121. [Fact]
  122. public void Changing_VirtualizationMode_None_To_Simple_Should_Update_Control()
  123. {
  124. var target = CreateTarget(mode: ItemVirtualizationMode.None);
  125. var scroll = (ScrollContentPresenter)target.Parent;
  126. scroll.Measure(new Size(100, 100));
  127. scroll.Arrange(new Rect(0, 0, 100, 100));
  128. Assert.Equal(20, target.Panel.Children.Count);
  129. Assert.Equal(new Size(10, 200), scroll.Extent);
  130. Assert.Equal(new Size(100, 100), scroll.Viewport);
  131. target.VirtualizationMode = ItemVirtualizationMode.Simple;
  132. target.Measure(new Size(100, 100));
  133. target.Arrange(new Rect(0, 0, 100, 100));
  134. Assert.Equal(10, target.Panel.Children.Count);
  135. Assert.Equal(new Size(0, 20), scroll.Extent);
  136. Assert.Equal(new Size(0, 10), scroll.Viewport);
  137. }
  138. [Fact]
  139. public void Changing_VirtualizationMode_None_To_Simple_Should_Add_Correct_Number_Of_Controls()
  140. {
  141. using (UnitTestApplication.Start(TestServices.RealLayoutManager))
  142. {
  143. var target = CreateTarget(mode: ItemVirtualizationMode.None);
  144. var scroll = (ScrollContentPresenter)target.Parent;
  145. scroll.Measure(new Size(100, 100));
  146. scroll.Arrange(new Rect(0, 0, 100, 100));
  147. // Ensure than an intermediate measure pass doesn't add more controls than it
  148. // should. This can happen if target gets measured with Size.Infinity which
  149. // is what the available size should be when VirtualizationMode == None but not
  150. // what it should after VirtualizationMode is changed to Simple.
  151. target.Panel.Children.CollectionChanged += (s, e) =>
  152. {
  153. Assert.InRange(target.Panel.Children.Count, 0, 10);
  154. };
  155. target.VirtualizationMode = ItemVirtualizationMode.Simple;
  156. LayoutManager.Instance.ExecuteLayoutPass();
  157. Assert.Equal(10, target.Panel.Children.Count);
  158. }
  159. }
  160. [Fact]
  161. public void Changing_VirtualizationMode_Simple_To_None_Should_Update_Control()
  162. {
  163. var target = CreateTarget();
  164. var scroll = (ScrollContentPresenter)target.Parent;
  165. scroll.Measure(new Size(100, 100));
  166. scroll.Arrange(new Rect(0, 0, 100, 100));
  167. Assert.Equal(10, target.Panel.Children.Count);
  168. Assert.Equal(new Size(0, 20), scroll.Extent);
  169. Assert.Equal(new Size(0, 10), scroll.Viewport);
  170. target.VirtualizationMode = ItemVirtualizationMode.None;
  171. target.Measure(new Size(100, 100));
  172. target.Arrange(new Rect(0, 0, 100, 100));
  173. // Here - unlike changing the other way - we need to do a layout pass on the scroll
  174. // content presenter as non-logical scroll values are only updated on arrange.
  175. scroll.Measure(new Size(100, 100));
  176. scroll.Arrange(new Rect(0, 0, 100, 100));
  177. Assert.Equal(20, target.Panel.Children.Count);
  178. Assert.Equal(new Size(10, 200), scroll.Extent);
  179. Assert.Equal(new Size(100, 100), scroll.Viewport);
  180. }
  181. private static ItemsPresenter CreateTarget(
  182. ItemVirtualizationMode mode = ItemVirtualizationMode.Simple,
  183. Orientation orientation = Orientation.Vertical,
  184. bool useContainers = true,
  185. int itemCount = 20)
  186. {
  187. ItemsPresenter result;
  188. var items = Enumerable.Range(0, itemCount).Select(x => $"Item {x}").ToList();
  189. var scroller = new ScrollContentPresenter
  190. {
  191. Content = result = new TestItemsPresenter(useContainers)
  192. {
  193. Items = items,
  194. ItemsPanel = VirtualizingPanelTemplate(orientation),
  195. ItemTemplate = ItemTemplate(),
  196. VirtualizationMode = mode,
  197. }
  198. };
  199. scroller.UpdateChild();
  200. return result;
  201. }
  202. private static IDataTemplate ItemTemplate()
  203. {
  204. return new FuncDataTemplate<string>(x => new Canvas
  205. {
  206. Width = 10,
  207. Height = 10,
  208. });
  209. }
  210. private static ITemplate<IPanel> VirtualizingPanelTemplate(
  211. Orientation orientation = Orientation.Vertical)
  212. {
  213. return new FuncTemplate<IPanel>(() => new VirtualizingStackPanel
  214. {
  215. Orientation = orientation,
  216. });
  217. }
  218. private class TestItemsPresenter : ItemsPresenter
  219. {
  220. private bool _useContainers;
  221. public TestItemsPresenter(bool useContainers)
  222. {
  223. _useContainers = useContainers;
  224. }
  225. protected override IItemContainerGenerator CreateItemContainerGenerator()
  226. {
  227. return _useContainers ?
  228. new ItemContainerGenerator<TestContainer>(this, TestContainer.ContentProperty, null) :
  229. new ItemContainerGenerator(this);
  230. }
  231. }
  232. private class TestContainer : ContentControl
  233. {
  234. public TestContainer()
  235. {
  236. Width = 10;
  237. Height = 10;
  238. }
  239. }
  240. }
  241. }