From 873bb90ef916feb0b7e86f062884009efd9f414f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartosz=20Korczy=C5=84ski?= Date: Tue, 6 Feb 2024 02:18:22 +0000 Subject: [PATCH] Double BringIntoView in VirtualizingStackPanel (#14419) This fixes a case when elements have different widths and the BringIntoView wants to scroll horizontally due to custom TargetRect Co-authored-by: Max Katz --- .../VirtualizingStackPanel.cs | 6 +++ .../VirtualizingStackPanelTests.cs | 38 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs index feb40d7dd9..f702f09e8a 100644 --- a/src/Avalonia.Controls/VirtualizingStackPanel.cs +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -430,6 +430,12 @@ namespace Avalonia.Controls root.LayoutManager.ExecuteLayoutPass(); } + // During the previous BringIntoView, the scroll width extent might have been out of date if + // elements have different widths. Because of that, the ScrollViewer might not scroll to the correct offset. + // After the previous BringIntoView, Y offset should be correct and an extra layout pass has been executed, + // hence the width extent should be correct now, and we can try to scroll again. + scrollToElement.BringIntoView(); + _scrollToElement = null; _scrollToIndex = -1; return scrollToElement; diff --git a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs index 8e5aa2f737..1939b52afa 100644 --- a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs @@ -1116,6 +1116,44 @@ namespace Avalonia.Controls.UnitTests Assert.True(container.IsVisible); } + [Fact] + public void ScrollIntoView_With_TargetRect_Outside_Viewport_Should_Scroll_To_Item() + { + using var app = App(); + var items = Enumerable.Range(0, 101).Select(x => new ItemWithHeight(x, x * 100 + 1)); + var itemTemplate = new FuncDataTemplate((x, _) => + new Border + { + Height = 10, + [!Layoutable.WidthProperty] = new Binding("Height"), + }); + var (target, scroll, itemsControl) = CreateTarget( + items: items, + itemTemplate: itemTemplate, + styles: new[] + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(ScrollViewer.HorizontalScrollBarVisibilityProperty, ScrollBarVisibility.Visible), + } + } + }); + itemsControl.ContainerPrepared += (_, ev) => + { + ev.Container.AddHandler(Control.RequestBringIntoViewEvent, (_, e) => + { + var dataContext = e.TargetObject.DataContext as ItemWithHeight; + e.TargetRect = new Rect(dataContext.Height - 50, 0, 50, 10); + }); + }; + + target.ScrollIntoView(100); + + Assert.Equal(9901, scroll.Offset.X); + } + private static IReadOnlyList GetRealizedIndexes(VirtualizingStackPanel target, ItemsControl itemsControl) { return target.GetRealizedElements()