Browse Source
Fix ListBox crash when scrolling after item removal (#21838)
* Add regression test for ListBox removal scroll crash
* Fix virtualized arrange after collection changes
pull/21852/head
Nathan Nguyen
2 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with
32 additions and
0 deletions
-
src/Avalonia.Controls/VirtualizingStackPanel.cs
-
tests/Avalonia.Controls.UnitTests/ListBoxVirtualizationIssueTests.cs
|
|
@ -251,6 +251,13 @@ namespace Avalonia.Controls |
|
|
var orientation = Orientation; |
|
|
var orientation = Orientation; |
|
|
var u = _realizedElements!.StartU; |
|
|
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) |
|
|
for (var i = 0; i < _realizedElements.Count; ++i) |
|
|
{ |
|
|
{ |
|
|
var e = _realizedElements.Elements[i]; |
|
|
var e = _realizedElements.Elements[i]; |
|
|
|
|
|
@ -11,6 +11,31 @@ namespace Avalonia.Controls.UnitTests; |
|
|
|
|
|
|
|
|
public class ListBoxVirtualizationIssueTests : ScopedTestBase |
|
|
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<int>(Enumerable.Range(0, 100)); |
|
|
|
|
|
var target = new ListBox |
|
|
|
|
|
{ |
|
|
|
|
|
Template = new FuncControlTemplate(CreateListBoxTemplate), |
|
|
|
|
|
ItemsSource = items, |
|
|
|
|
|
ItemTemplate = new FuncDataTemplate<int>((_, _) => new TextBlock { Height = 50 }), |
|
|
|
|
|
ItemsPanel = new FuncTemplate<Panel?>(() => new VirtualizingStackPanel()), |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
Prepare(target); |
|
|
|
|
|
target.ScrollIntoView(99); |
|
|
|
|
|
|
|
|
|
|
|
items.RemoveAt(0); |
|
|
|
|
|
target.ScrollIntoView(0); |
|
|
|
|
|
|
|
|
|
|
|
var firstContainer = Assert.IsType<ListBoxItem>(target.ContainerFromIndex(0)); |
|
|
|
|
|
Assert.Equal(1, firstContainer.Content); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
[Fact] |
|
|
[Fact] |
|
|
public void Replaced_ItemsSource_Should_Not_Show_Old_Selected_Item_When_Scrolled_Back() |
|
|
public void Replaced_ItemsSource_Should_Not_Show_Old_Selected_Item_When_Scrolled_Back() |
|
|
{ |
|
|
{ |
|
|
|