@ -23,6 +23,13 @@ namespace Avalonia.Controls.UnitTests
{
public class VirtualizingStackPanelTests : ScopedTestBase
{
private static FuncDataTemplate < ItemWithHeight > CanvasWithHeightTemplate = new ( ( _ , _ ) = >
new Canvas
{
Width = 1 0 0 ,
[!Layoutable.HeightProperty] = new Binding ( "Height" ) ,
} ) ;
[Fact]
public void Creates_Initial_Items ( )
{
@ -744,14 +751,7 @@ namespace Avalonia.Controls.UnitTests
var items = Enumerable . Range ( 0 , 1 0 0 0 ) . Select ( x = > new ItemWithHeight ( x ) ) . ToList ( ) ;
items [ 2 0 ] . Height = 2 0 0 ;
var itemTemplate = new FuncDataTemplate < ItemWithHeight > ( ( x , _ ) = >
new Canvas
{
Width = 1 0 0 ,
[!Canvas.HeightProperty] = new Binding ( "Height" ) ,
} ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : itemTemplate ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : CanvasWithHeightTemplate ) ;
var index = target . FirstRealizedIndex ;
@ -780,14 +780,7 @@ namespace Avalonia.Controls.UnitTests
var items = Enumerable . Range ( 0 , 1 0 0 ) . Select ( x = > new ItemWithHeight ( x ) ) . ToList ( ) ;
items [ 2 0 ] . Height = 2 0 0 ;
var itemTemplate = new FuncDataTemplate < ItemWithHeight > ( ( x , _ ) = >
new Canvas
{
Width = 1 0 0 ,
[!Canvas.HeightProperty] = new Binding ( "Height" ) ,
} ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : itemTemplate ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : CanvasWithHeightTemplate ) ;
// Scroll past the larger element.
scroll . Offset = new Vector ( 0 , 6 0 0 ) ;
@ -817,14 +810,7 @@ namespace Avalonia.Controls.UnitTests
var items = Enumerable . Range ( 0 , 1 0 0 ) . Select ( x = > new ItemWithHeight ( x , 3 0 ) ) . ToList ( ) ;
items [ 2 0 ] . Height = 2 5 ;
var itemTemplate = new FuncDataTemplate < ItemWithHeight > ( ( x , _ ) = >
new Canvas
{
Width = 1 0 0 ,
[!Canvas.HeightProperty] = new Binding ( "Height" ) ,
} ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : itemTemplate ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : CanvasWithHeightTemplate ) ;
// Scroll past the larger element.
scroll . Offset = new Vector ( 0 , 2 5 * items [ 0 ] . Height ) ;
@ -1154,6 +1140,58 @@ namespace Avalonia.Controls.UnitTests
Assert . Equal ( 9 9 0 1 , scroll . Offset . X ) ;
}
[Fact]
public void ScrollIntoView_Correctly_Scrolls_Down_To_A_Page_Of_Smaller_Items ( )
{
using var app = App ( ) ;
// First 10 items have height of 20, next 10 have height of 10.
var items = Enumerable . Range ( 0 , 2 0 ) . Select ( x = > new ItemWithHeight ( x , ( ( 2 9 - x ) / 1 0 ) * 1 0 ) ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : CanvasWithHeightTemplate ) ;
// Scroll the last item into view.
target . ScrollIntoView ( 1 9 ) ;
// At the time of the scroll, the average item height is 20, so the requested item
// should be placed at 380 (19 * 20) which therefore results in an extent of 390 to
// accommodate the item height of 10. This is obviously not a perfect answer, but
// it's the best we can do without knowing the actual item heights.
var container = Assert . IsType < ContentPresenter > ( target . ContainerFromIndex ( 1 9 ) ) ;
Assert . Equal ( new Rect ( 0 , 3 8 0 , 1 0 0 , 1 0 ) , container . Bounds ) ;
Assert . Equal ( new Size ( 1 0 0 , 1 0 0 ) , scroll . Viewport ) ;
Assert . Equal ( new Size ( 1 0 0 , 3 9 0 ) , scroll . Extent ) ;
Assert . Equal ( new Vector ( 0 , 2 9 0 ) , scroll . Offset ) ;
// Items 10-19 should be visible.
AssertRealizedItems ( target , itemsControl , 1 0 , 1 0 ) ;
}
[Fact]
public void ScrollIntoView_Correctly_Scrolls_Down_To_A_Page_Of_Larger_Items ( )
{
using var app = App ( ) ;
// First 10 items have height of 10, next 10 have height of 20.
var items = Enumerable . Range ( 0 , 2 0 ) . Select ( x = > new ItemWithHeight ( x , ( ( x / 1 0 ) + 1 ) * 1 0 ) ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : CanvasWithHeightTemplate ) ;
// Scroll the last item into view.
target . ScrollIntoView ( 1 9 ) ;
// At the time of the scroll, the average item height is 10, so the requested item
// should be placed at 190 (19 * 10) which therefore results in an extent of 210 to
// accommodate the item height of 20. This is obviously not a perfect answer, but
// it's the best we can do without knowing the actual item heights.
var container = Assert . IsType < ContentPresenter > ( target . ContainerFromIndex ( 1 9 ) ) ;
Assert . Equal ( new Rect ( 0 , 1 9 0 , 1 0 0 , 2 0 ) , container . Bounds ) ;
Assert . Equal ( new Size ( 1 0 0 , 1 0 0 ) , scroll . Viewport ) ;
Assert . Equal ( new Size ( 1 0 0 , 2 1 0 ) , scroll . Extent ) ;
Assert . Equal ( new Vector ( 0 , 1 1 0 ) , scroll . Offset ) ;
// Items 15-19 should be visible.
AssertRealizedItems ( target , itemsControl , 1 5 , 5 ) ;
}
private static IReadOnlyList < int > GetRealizedIndexes ( VirtualizingStackPanel target , ItemsControl itemsControl )
{
return target . GetRealizedElements ( )
@ -1176,6 +1214,11 @@ namespace Avalonia.Controls.UnitTests
. OrderBy ( x = > x )
. ToList ( ) ;
Assert . Equal ( Enumerable . Range ( firstIndex , count ) , childIndexes ) ;
var visibleChildren = target . Children
. Where ( x = > x . IsVisible )
. ToList ( ) ;
Assert . Equal ( count , visibleChildren . Count ) ;
}
private static void AssertRealizedControlItems < TContainer > (