diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 27a29a1e04..a54e502033 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -9,6 +9,7 @@ using Avalonia.Controls.Utils; using Avalonia.Input; using Avalonia.Layout; using Avalonia.Utilities; +using Avalonia.VisualTree; namespace Avalonia.Controls.Presenters { @@ -245,7 +246,7 @@ namespace Avalonia.Controls.Presenters var generator = Owner.ItemContainerGenerator; var panel = VirtualizingPanel; - if (!panel.IsFull && Items != null) + if (!panel.IsFull && Items != null && panel.IsAttachedToVisualTree) { var memberSelector = Owner.MemberSelector; var index = NextIndex; diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index db2bb0f886..7e1347daf5 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -194,6 +194,10 @@ namespace Avalonia.Controls.UnitTests private void Prepare(ListBox target) { + // The ListBox needs to be part of a rooted visual tree. + var root = new TestRoot(); + root.Child = target; + // Apply the template to the ListBox itself. target.ApplyTemplate(); diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs index 26657a0fbb..2f6361a867 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using System.Collections.Generic; using System.Linq; using Avalonia.Controls.Generators; @@ -8,6 +9,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Layout; +using Avalonia.Rendering; using Avalonia.UnitTests; using Xunit; @@ -15,6 +17,43 @@ namespace Avalonia.Controls.UnitTests.Presenters { public class ItemsPresenterTests_Virtualization { + [Fact] + public void Should_Not_Create_Items_Before_Added_To_Visual_Tree() + { + var items = Enumerable.Range(0, 10).Select(x => $"Item {x}").ToList(); + var target = new TestItemsPresenter(true) + { + Items = items, + ItemsPanel = VirtualizingPanelTemplate(Orientation.Vertical), + ItemTemplate = ItemTemplate(), + VirtualizationMode = ItemVirtualizationMode.Simple, + }; + + var scroller = new ScrollContentPresenter + { + Content = target, + }; + + scroller.UpdateChild(); + target.ApplyTemplate(); + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + Assert.Empty(target.Panel.Children); + + var root = new TestRoot + { + Child = scroller, + }; + + target.InvalidateMeasure(); + target.Panel.InvalidateMeasure(); + target.Measure(new Size(100, 100)); + target.Arrange(new Rect(0, 0, 100, 100)); + + Assert.Equal(10, target.Panel.Children.Count); + } + [Fact] public void Should_Return_IsLogicalScrollEnabled_False_When_Has_No_Virtualizing_Panel() { @@ -239,7 +278,7 @@ namespace Avalonia.Controls.UnitTests.Presenters ItemsPresenter result; var items = Enumerable.Range(0, itemCount).Select(x => $"Item {x}").ToList(); - var scroller = new ScrollContentPresenter + var scroller = new TestScroller { Content = result = new TestItemsPresenter(useContainers) { @@ -273,6 +312,21 @@ namespace Avalonia.Controls.UnitTests.Presenters }); } + private class TestScroller : ScrollContentPresenter, IRenderRoot + { + public IRenderQueueManager RenderQueueManager { get; } + + public Point PointToClient(Point point) + { + throw new NotImplementedException(); + } + + public Point PointToScreen(Point point) + { + throw new NotImplementedException(); + } + } + private class TestItemsPresenter : ItemsPresenter { private bool _useContainers; diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs index aa02ecb666..34b074d185 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -11,6 +12,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Input; +using Avalonia.Rendering; using Avalonia.UnitTests; using Xunit; @@ -730,7 +732,7 @@ namespace Avalonia.Controls.UnitTests.Presenters (IEnumerable)new AvaloniaList(itemsSource) : (IEnumerable)new ObservableCollection(itemsSource); - var scroller = new ScrollContentPresenter + var scroller = new TestScroller { Content = result = new TestItemsPresenter(useContainers) { @@ -764,6 +766,21 @@ namespace Avalonia.Controls.UnitTests.Presenters }); } + private class TestScroller : ScrollContentPresenter, IRenderRoot + { + public IRenderQueueManager RenderQueueManager { get; } + + public Point PointToClient(Point point) + { + throw new NotImplementedException(); + } + + public Point PointToScreen(Point point) + { + throw new NotImplementedException(); + } + } + private class TestItemsPresenter : ItemsPresenter { private bool _useContainers;