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()