From 4fc9848bbacf96c67326d6e299bacd11c5df7a5c Mon Sep 17 00:00:00 2001 From: Nathan Nguyen Date: Sat, 25 Jul 2026 01:59:16 +1000 Subject: [PATCH] Fix ListBox crash when scrolling after item removal (#21838) * Add regression test for ListBox removal scroll crash * Fix virtualized arrange after collection changes --- .../VirtualizingStackPanel.cs | 7 ++++++ .../ListBoxVirtualizationIssueTests.cs | 25 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs index 669e75d81a..a8cc7b669d 100644 --- a/src/Avalonia.Controls/VirtualizingStackPanel.cs +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -251,6 +251,13 @@ namespace Avalonia.Controls var orientation = Orientation; var u = _realizedElements!.StartU; + // Collection changes before the realized range make its exact position unstable + // until the next measure. ScrollIntoView can intentionally defer that measure + // while waiting for an updated viewport, so use the same position estimate used + // when realizing an element instead of arranging the range at NaN. + if (double.IsNaN(u)) + u = GetOrEstimateElementU(_realizedElements.FirstIndex); + for (var i = 0; i < _realizedElements.Count; ++i) { var e = _realizedElements.Elements[i]; diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxVirtualizationIssueTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxVirtualizationIssueTests.cs index 54fec22e44..d725c406c9 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxVirtualizationIssueTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxVirtualizationIssueTests.cs @@ -11,6 +11,31 @@ namespace Avalonia.Controls.UnitTests; public class ListBoxVirtualizationIssueTests : ScopedTestBase { + [Fact] + public void Removing_First_Item_After_Scrolling_To_End_Should_Allow_Scrolling_To_Start() + { + using (UnitTestApplication.Start(TestServices.MockPlatformRenderInterface)) + { + var items = new ObservableCollection(Enumerable.Range(0, 100)); + var target = new ListBox + { + Template = new FuncControlTemplate(CreateListBoxTemplate), + ItemsSource = items, + ItemTemplate = new FuncDataTemplate((_, _) => new TextBlock { Height = 50 }), + ItemsPanel = new FuncTemplate(() => new VirtualizingStackPanel()), + }; + + Prepare(target); + target.ScrollIntoView(99); + + items.RemoveAt(0); + target.ScrollIntoView(0); + + var firstContainer = Assert.IsType(target.ContainerFromIndex(0)); + Assert.Equal(1, firstContainer.Content); + } + } + [Fact] public void Replaced_ItemsSource_Should_Not_Show_Old_Selected_Item_When_Scrolled_Back() {