diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs index c514e0e85f..35107a84fc 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator`1.cs @@ -84,6 +84,12 @@ namespace Avalonia.Controls.Generators IMemberSelector selector) { var container = ContainerFromIndex(oldIndex); + + if (container == null) + { + throw new IndexOutOfRangeException("Could not recycle container: not materialized."); + } + var i = selector != null ? selector.Select(item) : item; container.SetValue(ContentProperty, i); diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 45c1c927e9..3c972e922f 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -105,22 +105,48 @@ namespace Avalonia.Controls.Presenters if (!panel.IsFull && Items != null) { - var index = NextIndex; - var items = Items.Cast().Skip(index); var memberSelector = Owner.MemberSelector; + var index = NextIndex; + var step = 1; - foreach (var item in items) + while (!panel.IsFull) { - var materialized = generator.Materialize(index++, item, memberSelector); - panel.Children.Add(materialized.ContainerControl); + if (index == ItemCount) + { + if (FirstIndex == 0) + { + break; + } + else + { + index = FirstIndex - 1; + step = -1; + } + } + + var materialized = generator.Materialize(index, Items.ElementAt(index), memberSelector); - if (panel.IsFull) + if (step == 1) { - break; + panel.Children.Add(materialized.ContainerControl); } + else + { + panel.Children.Insert(0, materialized.ContainerControl); + } + + index += step; } - NextIndex = index; + if (step == 1) + { + NextIndex = index; + } + else + { + NextIndex = ItemCount; + FirstIndex = index + 1; + } } if (panel.OverflowCount > 0) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs index 3404b65f52..013487f8fc 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs @@ -74,6 +74,29 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.Equal(8, target.Panel.Children.Count); } + [Fact] + public void Should_Add_New_Items_At_Top_When_Control_Is_Scrolled_To_Bottom_And_Enlarged() + { + var target = CreateTarget(); + var items = (IList)target.Items; + + target.ApplyTemplate(); + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + Assert.Equal(10, target.Panel.Children.Count); + + ((IScrollable)target).Offset = new Vector(0, 10); + target.Arrange(new Rect(0, 0, 100, 120)); + + Assert.Equal(12, target.Panel.Children.Count); + + for (var i = 0; i < target.Panel.Children.Count; ++i) + { + Assert.Equal(items[i + 8], target.Panel.Children[i].DataContext); + } + } + [Fact] public void Should_Update_Containers_When_Items_Changes() {