diff --git a/src/Avalonia.Controls/IVirtualizingPanel.cs b/src/Avalonia.Controls/IVirtualizingPanel.cs
index 2aa356d38c..ce320c0da7 100644
--- a/src/Avalonia.Controls/IVirtualizingPanel.cs
+++ b/src/Avalonia.Controls/IVirtualizingPanel.cs
@@ -30,6 +30,16 @@ namespace Avalonia.Controls
///
double AverageItemSize { get; }
+ ///
+ /// Gets or sets a size in pixels by which the content is overflowing the panel, in the
+ /// direction of scroll.
+ ///
+ ///
+ /// This may be non-zero even when is zero if the last item
+ /// overflows the panel bounds.
+ ///
+ double PixelOverflow { get; }
+
///
/// Gets or sets the current pixel offset of the items in the direction of scroll.
///
diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
index a92990b3ba..a7d1de5777 100644
--- a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
+++ b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections;
using System.Collections.Specialized;
using Avalonia.Controls.Primitives;
+using Avalonia.Controls.Utils;
using Avalonia.VisualTree;
namespace Avalonia.Controls.Presenters
@@ -19,14 +20,32 @@ namespace Avalonia.Controls.Presenters
public ItemsPresenter Owner { get; }
public IVirtualizingPanel VirtualizingPanel => Owner.Panel as IVirtualizingPanel;
public IEnumerable Items { get; private set; }
+ public int ItemCount { get; private set; }
public int FirstIndex { get; set; }
- public int LastIndex { get; set; } = -1;
+ public int NextIndex { get; set; }
+ public bool Vertical => VirtualizingPanel.ScrollDirection == Orientation.Vertical;
public abstract bool IsLogicalScrollEnabled { get; }
- public abstract Size Extent { get; }
- public abstract Vector Offset { get; set; }
- public abstract Size Viewport { get; }
+ public abstract double ExtentValue { get; }
+ public abstract double OffsetValue { get; set; }
+ public abstract double ViewportValue { get; }
+ public Size Extent => Vertical ? new Size(0, ExtentValue) : new Size(ExtentValue, 0);
+ public Size Viewport => Vertical ? new Size(0, ViewportValue) : new Size(ViewportValue, 0);
+
+ public Vector Offset
+ {
+ get
+ {
+ return Vertical ? new Vector(0, OffsetValue) : new Vector(OffsetValue, 0);
+ }
+
+ set
+ {
+ OffsetValue = Vertical ? value.Y : value.X;
+ }
+ }
+
public static ItemVirtualizer Create(ItemsPresenter owner)
{
var virtualizingPanel = owner.Panel as IVirtualizingPanel;
@@ -54,6 +73,7 @@ namespace Avalonia.Controls.Presenters
public virtual void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e)
{
Items = items;
+ ItemCount = items.Count();
}
}
}
diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
index 919bde7e0f..a8c24fcf72 100644
--- a/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
+++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs
@@ -19,18 +19,18 @@ namespace Avalonia.Controls.Presenters
public override bool IsLogicalScrollEnabled => false;
- public override Size Extent
+ public override double ExtentValue
{
get { throw new NotSupportedException(); }
}
- public override Vector Offset
+ public override double OffsetValue
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
- public override Size Viewport
+ public override double ViewportValue
{
get { throw new NotSupportedException(); }
}
diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
index 41e7c1b072..45c1c927e9 100644
--- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
+++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
@@ -19,64 +19,59 @@ namespace Avalonia.Controls.Presenters
public override bool IsLogicalScrollEnabled => true;
- public override Size Extent
- {
- get
- {
- if (VirtualizingPanel.ScrollDirection == Orientation.Vertical)
- {
- return new Size(0, Items.Count());
- }
- else
- {
- return new Size(Items.Count(), 0);
- }
- }
- }
+ public override double ExtentValue => ItemCount;
- public override Vector Offset
+ public override double OffsetValue
{
get
{
- if (VirtualizingPanel.ScrollDirection == Orientation.Vertical)
- {
- return new Vector(0, FirstIndex);
- }
- else
- {
- return new Vector(FirstIndex, 0);
- }
+ var offset = VirtualizingPanel.PixelOffset > 0 ? 1 : 0;
+ return FirstIndex + offset;
}
set
{
- var scroll = (VirtualizingPanel.ScrollDirection == Orientation.Vertical) ?
- value.Y : value.X;
- var delta = (int)(scroll - FirstIndex);
+ var panel = VirtualizingPanel;
+ var offset = VirtualizingPanel.PixelOffset > 0 ? 1 : 0;
+ var delta = (int)(value - (FirstIndex + offset));
if (delta != 0)
{
- RecycleContainers(delta);
- FirstIndex += delta;
- LastIndex += delta;
+ if ((NextIndex - 1) + delta < ItemCount)
+ {
+ if (panel.PixelOffset > 0)
+ {
+ panel.PixelOffset = 0;
+ delta += 1;
+ }
+
+ if (delta != 0)
+ {
+ RecycleContainers(delta);
+ FirstIndex += delta;
+ NextIndex += delta;
+ }
+ }
+ else
+ {
+ // We're moving to a partially obscured item at the end of the list.
+ var firstIndex = ItemCount - panel.Children.Count;
+ RecycleContainers(firstIndex - FirstIndex);
+ NextIndex = ItemCount;
+ FirstIndex = NextIndex - panel.Children.Count;
+ panel.PixelOffset = VirtualizingPanel.PixelOverflow;
+ }
}
}
}
- public override Size Viewport
+ public override double ViewportValue
{
get
{
- var panel = VirtualizingPanel;
-
- if (panel.ScrollDirection == Orientation.Vertical)
- {
- return new Size(0, panel.Children.Count);
- }
- else
- {
- return new Size(panel.Children.Count, 0);
- }
+ // If we can't fit the last item in the panel fully, subtract 1 from the viewport.
+ var overflow = VirtualizingPanel.PixelOverflow > 0 ? 1 : 0;
+ return VirtualizingPanel.Children.Count - overflow;
}
}
@@ -96,8 +91,7 @@ namespace Avalonia.Controls.Presenters
// Reset indicates a large change and should (?) be quite rare.
VirtualizingPanel.Children.Clear();
Owner.ItemContainerGenerator.Clear();
- FirstIndex = 0;
- LastIndex = -1;
+ FirstIndex = NextIndex = 0;
CreateRemoveContainers();
}
@@ -111,7 +105,7 @@ namespace Avalonia.Controls.Presenters
if (!panel.IsFull && Items != null)
{
- var index = LastIndex + 1;
+ var index = NextIndex;
var items = Items.Cast