@ -1192,6 +1192,112 @@ namespace Avalonia.Controls.UnitTests
AssertRealizedItems ( target , itemsControl , 1 5 , 5 ) ;
}
[Fact]
public void Extent_And_Offset_Should_Be_Updated_When_Containers_Resize ( )
{
using var app = App ( ) ;
// All containers start off with a height of 50 (2 containers fit in viewport).
var items = Enumerable . Range ( 0 , 2 0 ) . Select ( x = > new ItemWithHeight ( x , 5 0 ) ) . ToList ( ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : CanvasWithHeightTemplate ) ;
// Scroll to the 5th item (containers 4 and 5 should be visible).
target . ScrollIntoView ( 5 ) ;
Assert . Equal ( 4 , target . FirstRealizedIndex ) ;
Assert . Equal ( 5 , target . LastRealizedIndex ) ;
// The extent should be 500 (10 * 50) and the offset should be 200 (4 * 50).
var container = Assert . IsType < ContentPresenter > ( target . ContainerFromIndex ( 5 ) ) ;
Assert . Equal ( new Rect ( 0 , 2 5 0 , 1 0 0 , 5 0 ) , container . Bounds ) ;
Assert . Equal ( new Size ( 1 0 0 , 1 0 0 ) , scroll . Viewport ) ;
Assert . Equal ( new Size ( 1 0 0 , 1 0 0 0 ) , scroll . Extent ) ;
Assert . Equal ( new Vector ( 0 , 2 0 0 ) , scroll . Offset ) ;
// Update the height of all items to 25 and run a layout pass.
foreach ( var item in items )
item . Height = 2 5 ;
target . UpdateLayout ( ) ;
// The extent should be updated to reflect the new heights. The offset should be
// unchanged but the first realized index should be updated to 8 (200 / 25).
Assert . Equal ( new Size ( 1 0 0 , 1 0 0 ) , scroll . Viewport ) ;
Assert . Equal ( new Size ( 1 0 0 , 5 0 0 ) , scroll . Extent ) ;
Assert . Equal ( new Vector ( 0 , 2 0 0 ) , scroll . Offset ) ;
Assert . Equal ( 8 , target . FirstRealizedIndex ) ;
Assert . Equal ( 1 1 , target . LastRealizedIndex ) ;
}
[Fact]
public void Focused_Container_Is_Positioned_Correctly_when_Container_Size_Change_Causes_It_To_Be_Moved_Out_Of_Visible_Viewport ( )
{
using var app = App ( ) ;
// All containers start off with a height of 50 (2 containers fit in viewport).
var items = Enumerable . Range ( 0 , 2 0 ) . Select ( x = > new ItemWithHeight ( x , 5 0 ) ) . ToList ( ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : CanvasWithHeightTemplate ) ;
// Scroll to the 5th item (containers 4 and 5 should be visible).
target . ScrollIntoView ( 5 ) ;
Assert . Equal ( 4 , target . FirstRealizedIndex ) ;
Assert . Equal ( 5 , target . LastRealizedIndex ) ;
// Focus the 5th item.
var container = Assert . IsType < ContentPresenter > ( target . ContainerFromIndex ( 5 ) ) ;
container . Focusable = true ;
container . Focus ( ) ;
// Update the height of all items to 25 and run a layout pass.
foreach ( var item in items )
item . Height = 2 5 ;
target . UpdateLayout ( ) ;
// The focused container should now be outside the realized range.
Assert . Equal ( 8 , target . FirstRealizedIndex ) ;
Assert . Equal ( 1 1 , target . LastRealizedIndex ) ;
// The container should still exist and be positioned outside the visible viewport.
container = Assert . IsType < ContentPresenter > ( target . ContainerFromIndex ( 5 ) ) ;
Assert . Equal ( new Rect ( 0 , 1 2 5 , 1 0 0 , 2 5 ) , container . Bounds ) ;
}
[Fact]
public void Focused_Container_Is_Positioned_Correctly_when_Container_Size_Change_Causes_It_To_Be_Moved_Into_Visible_Viewport ( )
{
using var app = App ( ) ;
// All containers start off with a height of 25 (4 containers fit in viewport).
var items = Enumerable . Range ( 0 , 2 0 ) . Select ( x = > new ItemWithHeight ( x , 2 5 ) ) . ToList ( ) ;
var ( target , scroll , itemsControl ) = CreateTarget ( items : items , itemTemplate : CanvasWithHeightTemplate ) ;
// Scroll to the 5th item (containers 4-7 should be visible).
target . ScrollIntoView ( 7 ) ;
Assert . Equal ( 4 , target . FirstRealizedIndex ) ;
Assert . Equal ( 7 , target . LastRealizedIndex ) ;
// Focus the 7th item.
var container = Assert . IsType < ContentPresenter > ( target . ContainerFromIndex ( 7 ) ) ;
container . Focusable = true ;
container . Focus ( ) ;
// Scroll up to the 3rd item (containers 3-6 should still be visible).
target . ScrollIntoView ( 3 ) ;
Assert . Equal ( 3 , target . FirstRealizedIndex ) ;
Assert . Equal ( 6 , target . LastRealizedIndex ) ;
// Update the height of all items to 20 and run a layout pass.
foreach ( var item in items )
item . Height = 2 0 ;
target . UpdateLayout ( ) ;
// The focused container should now be inside the realized range.
Assert . Equal ( 3 , target . FirstRealizedIndex ) ;
Assert . Equal ( 7 , target . LastRealizedIndex ) ;
// The container should be positioned correctly.
container = Assert . IsType < ContentPresenter > ( target . ContainerFromIndex ( 7 ) ) ;
Assert . Equal ( new Rect ( 0 , 1 4 0 , 1 0 0 , 2 0 ) , container . Bounds ) ;
}
private static IReadOnlyList < int > GetRealizedIndexes ( VirtualizingStackPanel target , ItemsControl itemsControl )
{
return target . GetRealizedElements ( )
@ -1354,8 +1460,10 @@ namespace Avalonia.Controls.UnitTests
private static IDisposable App ( ) = > UnitTestApplication . Start ( TestServices . RealFocus ) ;
private class ItemWithHeight
private class ItemWithHeight : NotifyingBase
{
private double _ height ;
public ItemWithHeight ( int index , double height = 1 0 )
{
Caption = $"Item {index}" ;
@ -1363,7 +1471,12 @@ namespace Avalonia.Controls.UnitTests
}
public string Caption { get ; set ; }
public double Height { get ; set ; }
public double Height
{
get = > _ height ;
set = > SetField ( ref _ height , value ) ;
}
}
private class ItemWithIsVisible : NotifyingBase