From 9f95bd9156a64b7a2af376cb98947895234bb867 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 3 Apr 2023 18:07:27 +0200 Subject: [PATCH] Recycle elements early when viewport is disjunct. --- .../VirtualizingStackPanel.cs | 11 +++++++ .../VirtualizingStackPanelTests.cs | 31 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs index 6045f85135..850511f5d1 100644 --- a/src/Avalonia.Controls/VirtualizingStackPanel.cs +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -166,6 +166,10 @@ namespace Avalonia.Controls // - Vertical layouts: U = vertical, V = horizontal var viewport = CalculateMeasureViewport(items); + // If the viewport is disjunct then we can recycle everything. + if (viewport.viewportIsDisjunct) + _realizedElements.RecycleAllElements(_recycleElement); + // Do the measure, creating/recycling elements as necessary to fill the viewport. Don't // write to _realizedElements yet, only _measureElements. RealizeElements(items, availableSize, ref viewport); @@ -409,6 +413,7 @@ namespace Avalonia.Controls var viewportStart = Orientation == Orientation.Horizontal ? viewport.X : viewport.Y; var viewportEnd = Orientation == Orientation.Horizontal ? viewport.Right : viewport.Bottom; + // Get or estimate the anchor element from which to start realization. var itemCount = items?.Count ?? 0; var (anchorIndex, anchorU) = _realizedElements.GetOrEstimateAnchorElementForViewport( viewportStart, @@ -416,12 +421,17 @@ namespace Avalonia.Controls itemCount, ref _lastEstimatedElementSizeU); + // Check if the anchor element is not within the currently realized elements. + var disjunct = anchorIndex < _realizedElements.FirstIndex || + anchorIndex > _realizedElements.LastIndex; + return new MeasureViewport { anchorIndex = anchorIndex, anchorU = anchorU, viewportUStart = viewportStart, viewportUEnd = viewportEnd, + viewportIsDisjunct = disjunct, }; } @@ -895,6 +905,7 @@ namespace Avalonia.Controls public double measuredV; public double realizedEndU; public int lastIndex; + public bool viewportIsDisjunct; } } } diff --git a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs index 2cdd4eaf95..e8b4b8c513 100644 --- a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs @@ -83,7 +83,7 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Scrolls_To_Index() + public void Scrolls_Down_To_Index() { using var app = App(); var (target, scroll, itemsControl) = CreateTarget(); @@ -93,6 +93,35 @@ namespace Avalonia.Controls.UnitTests AssertRealizedItems(target, itemsControl, 11, 10); } + [Fact] + public void Scrolls_Up_To_Index() + { + using var app = App(); + var (target, scroll, itemsControl) = CreateTarget(); + + scroll.ScrollToEnd(); + Layout(target); + + Assert.Equal(90, target.FirstRealizedIndex); + + target.ScrollIntoView(20); + + AssertRealizedItems(target, itemsControl, 20, 10); + } + + [Fact] + public void Scrolling_Up_To_Index_Does_Not_Create_A_Page_Of_Unrealized_Elements() + { + using var app = App(); + var (target, scroll, itemsControl) = CreateTarget(); + + scroll.ScrollToEnd(); + Layout(target); + target.ScrollIntoView(20); + + Assert.Equal(11, target.Children.Count); + } + [Fact] public void Creates_Elements_On_Item_Insert_1() {