diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs index 0580761631..2f275431ff 100644 --- a/src/Avalonia.Controls/VirtualizingStackPanel.cs +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -662,9 +662,12 @@ namespace Avalonia.Controls _scrollViewer?.UnregisterAnchorCandidate(element); var recycleKey = element.GetValue(RecycleKeyProperty); - Debug.Assert(recycleKey is not null); - if (recycleKey == s_itemIsItsOwnContainer) + if (recycleKey is null) + { + RemoveInternalChild(element); + } + else if (recycleKey == s_itemIsItsOwnContainer) { element.IsVisible = false; } @@ -687,9 +690,8 @@ namespace Avalonia.Controls Debug.Assert(ItemContainerGenerator is not null); var recycleKey = element.GetValue(RecycleKeyProperty); - Debug.Assert(recycleKey is not null); - - if (recycleKey == s_itemIsItsOwnContainer) + + if (recycleKey is null || recycleKey == s_itemIsItsOwnContainer) { RemoveInternalChild(element); } diff --git a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs index 1fddaab910..c5a6ecc376 100644 --- a/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs +++ b/tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs @@ -646,7 +646,7 @@ namespace Avalonia.Controls.UnitTests { // Issue #11272 using var app = App(); - var (_, _, itemsControl) = CreateUnrootedTarget(); + var (_, _, itemsControl) = CreateUnrootedTarget(); var container = new Decorator { Margin = new Thickness(100) }; var root = new TestRoot(true, container); @@ -657,11 +657,49 @@ namespace Avalonia.Controls.UnitTests root.LayoutManager.ExecuteLayoutPass(); } + [Fact] + public void Supports_Null_Recycle_Key_When_Scrolling() + { + using var app = App(); + var (_, scroll, itemsControl) = CreateUnrootedTarget(); + var root = CreateRoot(itemsControl); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + var firstItem = itemsControl.ContainerFromIndex(0)!; + scroll.Offset = new(0, 20); + + Layout(itemsControl); + + Assert.Null(firstItem.Parent); + Assert.Null(firstItem.VisualParent); + Assert.DoesNotContain(firstItem, itemsControl.ItemsPanelRoot!.Children); + } + + [Fact] + public void Supports_Null_Recycle_Key_When_Clearing_Items() + { + using var app = App(); + var (_, _, itemsControl) = CreateUnrootedTarget(); + var root = CreateRoot(itemsControl); + + root.LayoutManager.ExecuteInitialLayoutPass(); + + var firstItem = itemsControl.ContainerFromIndex(0)!; + itemsControl.ItemsSource = null; + + Layout(itemsControl); + + Assert.Null(firstItem.Parent); + Assert.Null(firstItem.VisualParent); + Assert.Empty(itemsControl.ItemsPanelRoot!.Children); + } + [Fact] public void ScrollIntoView_On_Effectively_Invisible_Panel_Does_Not_Create_Ghost_Elements() { var items = new[] { "foo", "bar", "baz" }; - var (target, _, itemsControl) = CreateUnrootedTarget(items: items); + var (target, _, itemsControl) = CreateUnrootedTarget(items: items); var container = new Decorator { Margin = new Thickness(100), Child = itemsControl }; var root = new TestRoot(true, container); @@ -738,7 +776,7 @@ namespace Avalonia.Controls.UnitTests Optional itemTemplate = default, IEnumerable