diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs index a7d1de5777..dd7cf3bf1d 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs @@ -10,29 +10,88 @@ using Avalonia.VisualTree; namespace Avalonia.Controls.Presenters { + /// + /// Base class for classes which handle virtualization for an . + /// internal abstract class ItemVirtualizer { + /// + /// Initializes a new instance of the class. + /// + /// public ItemVirtualizer(ItemsPresenter owner) { Owner = owner; } + /// + /// Gets the which owns the virtualizer. + /// public ItemsPresenter Owner { get; } + + /// + /// Gets the which will host the items. + /// public IVirtualizingPanel VirtualizingPanel => Owner.Panel as IVirtualizingPanel; + + /// + /// Gets the items to display. + /// public IEnumerable Items { get; private set; } + + /// + /// Gets the number of items in . + /// public int ItemCount { get; private set; } - public int FirstIndex { get; set; } - public int NextIndex { get; set; } + + /// + /// Gets or sets the index of the first item displayed in the panel. + /// + public int FirstIndex { get; protected set; } + + /// + /// Gets or sets the index of the first item beyond those displayed in the panel. + /// + public int NextIndex { get; protected set; } + + /// + /// Gets a value indicating whether the items should be scroll horizontally or vertically. + /// public bool Vertical => VirtualizingPanel.ScrollDirection == Orientation.Vertical; + /// + /// Gets a value indicating whether logical scrolling is enabled. + /// public abstract bool IsLogicalScrollEnabled { get; } + + /// + /// Gets the value of the scroll extent. + /// public abstract double ExtentValue { get; } + + /// + /// Gets or sets the value of the current scroll offset. + /// public abstract double OffsetValue { get; set; } + + /// + /// Gets the value of the scrollable viewport. + /// public abstract double ViewportValue { get; } + /// + /// Gets the as a . + /// public Size Extent => Vertical ? new Size(0, ExtentValue) : new Size(ExtentValue, 0); + + /// + /// Gets the as a . + /// public Size Viewport => Vertical ? new Size(0, ViewportValue) : new Size(ViewportValue, 0); + /// + /// Gets or sets the as a . + /// public Vector Offset { get @@ -46,6 +105,12 @@ namespace Avalonia.Controls.Presenters } } + /// + /// Creates an based on an item presenter's + /// . + /// + /// The items presenter. + /// An . public static ItemVirtualizer Create(ItemsPresenter owner) { var virtualizingPanel = owner.Panel as IVirtualizingPanel; @@ -63,13 +128,30 @@ namespace Avalonia.Controls.Presenters return new ItemVirtualizerNone(owner); } + /// + /// Called by the when it carries out an arrange. + /// + /// The final size passed to the arrange. public abstract void Arranging(Size finalSize); + /// + /// Called when a request is made to bring an item into view. + /// + /// The item to bring into view. + /// The rect on the item to bring into view. + /// True if the request was handled; otherwise false. public virtual bool BringIntoView(IVisual target, Rect targetRect) { return false; } + /// + /// Called when the items for the presenter change, either because + /// has been set, the items collection has been + /// modified, or the panel has been created. + /// + /// The items. + /// A description of the change. public virtual void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e) { Items = items; diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs index a8c24fcf72..303c7442c2 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerNone.cs @@ -10,6 +10,10 @@ using Avalonia.Controls.Utils; namespace Avalonia.Controls.Presenters { + /// + /// Represents an item virtualizer for an that doesn't actually + /// virtualize items - it just creates a container for every item. + /// internal class ItemVirtualizerNone : ItemVirtualizer { public ItemVirtualizerNone(ItemsPresenter owner) @@ -17,29 +21,44 @@ namespace Avalonia.Controls.Presenters { } + /// public override bool IsLogicalScrollEnabled => false; + /// + /// This property should never be accessed because is + /// false. + /// public override double ExtentValue { get { throw new NotSupportedException(); } } + /// + /// This property should never be accessed because is + /// false. + /// public override double OffsetValue { get { throw new NotSupportedException(); } set { throw new NotSupportedException(); } } + /// + /// This property should never be accessed because is + /// false. + /// public override double ViewportValue { get { throw new NotSupportedException(); } } + /// public override void Arranging(Size finalSize) { // We don't need to do anything here. } + /// public override void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e) { base.ItemsChanged(items, e); @@ -47,7 +66,6 @@ namespace Avalonia.Controls.Presenters var generator = Owner.ItemContainerGenerator; var panel = Owner.Panel; - // TODO: Handle Move and Replace etc. switch (e.Action) { case NotifyCollectionChangedAction.Add: @@ -77,7 +95,9 @@ namespace Avalonia.Controls.Presenters break; case NotifyCollectionChangedAction.Move: - // TODO: Implement Move in a more efficient manner. + // TODO: Handle move in a more efficient manner. At the moment we just + // drop through to Reset to recreate all the containers. + case NotifyCollectionChangedAction.Reset: RemoveContainers(generator.Clear()); diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs index 01b1b24e43..69141fdbea 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs @@ -10,17 +10,28 @@ using Avalonia.Controls.Utils; namespace Avalonia.Controls.Presenters { + /// + /// Handles virtualization in an for + /// . + /// internal class ItemVirtualizerSimple : ItemVirtualizer { + /// + /// Initializes a new instance of the class. + /// + /// public ItemVirtualizerSimple(ItemsPresenter owner) : base(owner) { } + /// public override bool IsLogicalScrollEnabled => true; + /// public override double ExtentValue => ItemCount; + /// public override double OffsetValue { get @@ -61,6 +72,7 @@ namespace Avalonia.Controls.Presenters } } + /// public override double ViewportValue { get @@ -71,12 +83,14 @@ namespace Avalonia.Controls.Presenters } } + /// public override void Arranging(Size finalSize) { CreateAndRemoveContainers(); ((ILogicalScrollable)Owner).InvalidateScroll(); } + /// public override void ItemsChanged(IEnumerable items, NotifyCollectionChangedEventArgs e) { base.ItemsChanged(items, e); @@ -123,6 +137,10 @@ namespace Avalonia.Controls.Presenters ((ILogicalScrollable)Owner).InvalidateScroll(); } + /// + /// Creates and removes containers such that we have at most enough containers to fill + /// the panel. + /// private void CreateAndRemoveContainers() { var generator = Owner.ItemContainerGenerator; @@ -184,6 +202,14 @@ namespace Avalonia.Controls.Presenters } } + /// + /// Updates the containers in the panel to make sure they are displaying the correct item + /// based on . + /// + /// + /// This method requires that + the number of + /// materialized containers is not more than . + /// private void RecycleContainers() { var panel = VirtualizingPanel; @@ -208,6 +234,19 @@ namespace Avalonia.Controls.Presenters } } + /// + /// Recycles containers when a move occurs. + /// + /// The delta of the move. + /// + /// If the move is less than a page, then this method moves the containers for the items + /// that are still visible to the correct place, and recyles and moves the others. For + /// example: if there are 20 items and 10 containers visible and the user scrolls 5 + /// items down, then the bottom 5 containers will be moved to the top and the top 5 will + /// be moved to the bottom and recycled to display the newly visible item. Updates + /// and + /// with their new values. + /// private void RecycleContainersForMove(int delta) { var panel = VirtualizingPanel; @@ -250,6 +289,9 @@ namespace Avalonia.Controls.Presenters NextIndex += delta; } + /// + /// Recycles containers due to items being removed. + /// private void RecycleContainersOnRemove() { var panel = VirtualizingPanel; @@ -283,6 +325,11 @@ namespace Avalonia.Controls.Presenters } } + /// + /// Removes the specified number of containers from the end of the panel and updates + /// . + /// + /// The number of containers to remove. private void RemoveContainers(int count) { var index = VirtualizingPanel.Children.Count - count;