Browse Source

Merge pull request #710 from AvaloniaUI/fix-virtualization

Fix virtualization
pull/727/head
danwalmsley 10 years ago
committed by GitHub
parent
commit
dfd4bbf34a
  1. 19
      src/Avalonia.Controls/IVirtualizingPanel.cs
  2. 5
      src/Avalonia.Controls/Platform/ITopLevelImpl.cs
  3. 5
      src/Avalonia.Controls/Platform/IWindowImpl.cs
  4. 3
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  5. 2
      src/Avalonia.Controls/Presenters/ItemsPresenter.cs
  6. 11
      src/Avalonia.Controls/VirtualizingStackPanel.cs
  7. 1
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs
  8. 62
      tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

19
src/Avalonia.Controls/IVirtualizingPanel.cs

@ -1,6 +1,8 @@
// 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 Avalonia.Layout;
namespace Avalonia.Controls
{
/// <summary>
@ -24,8 +26,8 @@ namespace Avalonia.Controls
/// </summary>
/// <remarks>
/// This property should return false until enough children are added to fill the space
/// passed into the last measure in the direction of scroll. It should be updated
/// immediately after a child is added or removed.
/// passed into the last measure or arrange in the direction of scroll. It should be
/// updated immediately after a child is added or removed.
/// </remarks>
bool IsFull { get; }
@ -63,5 +65,18 @@ namespace Avalonia.Controls
/// Gets or sets the current pixel offset of the items in the direction of scroll.
/// </summary>
double PixelOffset { get; set; }
/// <summary>
/// Invalidates the measure of the control and forces a call to
/// <see cref="IVirtualizingController.UpdateControls"/> on the next measure.
/// </summary>
/// <remarks>
/// The implementation for this method should call
/// <see cref="ILayoutable.InvalidateMeasure"/> and also ensure that the next call to
/// <see cref="ILayoutable.Measure(Size)"/> calls
/// <see cref="IVirtualizingController.UpdateControls"/> on the next measure even if
/// the available size hasn't changed.
/// </remarks>
void ForceInvalidateMeasure();
}
}

5
src/Avalonia.Controls/Platform/ITopLevelImpl.cs

@ -22,6 +22,11 @@ namespace Avalonia.Platform
/// </summary>
Size ClientSize { get; set; }
/// <summary>
/// Gets the maximum size of a window on the system.
/// </summary>
Size MaxClientSize { get; }
/// <summary>
/// Gets the scaling factor for the window.
/// </summary>

5
src/Avalonia.Controls/Platform/IWindowImpl.cs

@ -11,11 +11,6 @@ namespace Avalonia.Platform
/// </summary>
public interface IWindowImpl : ITopLevelImpl
{
/// <summary>
/// Gets the maximum size of a window on the system.
/// </summary>
Size MaxClientSize { get; }
/// <summary>
/// Gets or sets the minimized/maximized state of the window.
/// </summary>

3
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -121,6 +121,7 @@ namespace Avalonia.Controls.Presenters
RecycleContainers();
}
panel.ForceInvalidateMeasure();
break;
case NotifyCollectionChangedAction.Remove:
@ -130,6 +131,7 @@ namespace Avalonia.Controls.Presenters
RecycleContainersOnRemove();
}
panel.ForceInvalidateMeasure();
break;
case NotifyCollectionChangedAction.Move:
@ -140,6 +142,7 @@ namespace Avalonia.Controls.Presenters
case NotifyCollectionChangedAction.Reset:
RecycleContainersOnRemove();
CreateAndRemoveContainers();
panel.ForceInvalidateMeasure();
break;
}
}

2
src/Avalonia.Controls/Presenters/ItemsPresenter.cs

@ -99,7 +99,7 @@ namespace Avalonia.Controls.Presenters
// the available size.
if (availableSize == Size.Infinity && VirtualizationMode != ItemVirtualizationMode.None)
{
var window = VisualRoot as Window;
var window = VisualRoot as TopLevel;
if (window != null)
{

11
src/Avalonia.Controls/VirtualizingStackPanel.cs

@ -19,6 +19,7 @@ namespace Avalonia.Controls
private double _averageItemSize;
private int _averageCount;
private double _pixelOffset;
private bool _forceRemeasure;
bool IVirtualizingPanel.IsFull
{
@ -61,10 +62,17 @@ namespace Avalonia.Controls
private IVirtualizingController Controller => ((IVirtualizingPanel)this).Controller;
void IVirtualizingPanel.ForceInvalidateMeasure()
{
InvalidateMeasure();
_forceRemeasure = true;
}
protected override Size MeasureOverride(Size availableSize)
{
if (availableSize != ((ILayoutable)this).PreviousMeasure)
if (_forceRemeasure || availableSize != ((ILayoutable)this).PreviousMeasure)
{
_forceRemeasure = false;
_availableSpace = availableSize;
Controller?.UpdateControls();
}
@ -74,6 +82,7 @@ namespace Avalonia.Controls
protected override Size ArrangeOverride(Size finalSize)
{
_availableSpace = finalSize;
_canBeRemoved = 0;
_takenSpace = 0;
_averageItemSize = 0;

1
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@ -658,6 +658,7 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(expected, actual);
}
[Fact]
public void Should_Add_Containers_For_Items_After_Clear()
{
var target = CreateTarget(itemCount: 10);

62
tests/Avalonia.Controls.UnitTests/VirtualizingStackPanelTests.cs

@ -22,7 +22,51 @@ namespace Avalonia.Controls.UnitTests
target.Controller = controller.Object;
target.Measure(new Size(100, 100));
controller.Verify(x => x.UpdateControls());
controller.Verify(x => x.UpdateControls(), Times.Once());
}
[Fact]
public void Measure_Invokes_Controller_UpdateControls_If_AvailableSize_Changes()
{
var target = (IVirtualizingPanel)new VirtualizingStackPanel();
var controller = new Mock<IVirtualizingController>();
target.Controller = controller.Object;
target.Measure(new Size(100, 100));
target.InvalidateMeasure();
target.Measure(new Size(100, 100));
target.InvalidateMeasure();
target.Measure(new Size(100, 101));
controller.Verify(x => x.UpdateControls(), Times.Exactly(2));
}
[Fact]
public void Measure_Does_Not_Invoke_Controller_UpdateControls_If_AvailableSize_Is_The_Same()
{
var target = (IVirtualizingPanel)new VirtualizingStackPanel();
var controller = new Mock<IVirtualizingController>();
target.Controller = controller.Object;
target.Measure(new Size(100, 100));
target.InvalidateMeasure();
target.Measure(new Size(100, 100));
controller.Verify(x => x.UpdateControls(), Times.Once());
}
[Fact]
public void Measure_Invokes_Controller_UpdateControls_If_AvailableSize_Is_The_Same_After_ForceInvalidateMeasure()
{
var target = (IVirtualizingPanel)new VirtualizingStackPanel();
var controller = new Mock<IVirtualizingController>();
target.Controller = controller.Object;
target.Measure(new Size(100, 100));
target.ForceInvalidateMeasure();
target.Measure(new Size(100, 100));
controller.Verify(x => x.UpdateControls(), Times.Exactly(2));
}
[Fact]
@ -35,7 +79,7 @@ namespace Avalonia.Controls.UnitTests
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 110, 110));
controller.Verify(x => x.UpdateControls());
controller.Verify(x => x.UpdateControls(), Times.Exactly(2));
}
[Fact]
@ -118,6 +162,20 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(2, target.PixelOverflow);
}
[Fact]
public void Reports_PixelOverflow_After_Arrange_Smaller_Than_Measure()
{
var target = (IVirtualizingPanel)new VirtualizingStackPanel();
target.Children.Add(new Canvas { Width = 50, Height = 50 });
target.Children.Add(new Canvas { Width = 50, Height = 52 });
target.Measure(new Size(100, 100));
target.Arrange(new Rect(0, 0, 50, 50));
Assert.Equal(52, target.PixelOverflow);
}
[Fact]
public void Reports_PixelOverflow_With_PixelOffset()
{

Loading…
Cancel
Save