A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

148 lines
4.0 KiB

// 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.Controls.Primitives;
using System;
using System.Collections.Specialized;
namespace Avalonia.Controls
{
public class VirtualizingStackPanel : StackPanel, IScrollable, IVirtualizingPanel
{
private double _takenSpace;
private int _canBeRemoved;
bool IVirtualizingPanel.IsFull
{
get
{
return Orientation == Orientation.Horizontal ?
_takenSpace >= Bounds.Width :
_takenSpace >= Bounds.Height;
}
}
int IVirtualizingPanel.OverflowCount => _canBeRemoved;
Action IVirtualizingPanel.ArrangeCompleted { get; set; }
Action IScrollable.InvalidateScroll
{
get;
set;
}
Size IScrollable.Extent => new Size(_takenSpace, _takenSpace);
Vector IScrollable.Offset
{
get { return default(Vector); }
set { }
}
Size IScrollable.Viewport => Bounds.Size;
Size IScrollable.ScrollSize => new Size(1, 1);
Size IScrollable.PageScrollSize => new Size(1, 1);
protected override Size ArrangeOverride(Size finalSize)
{
_canBeRemoved = 0;
_takenSpace = 0;
var result = base.ArrangeOverride(finalSize);
((IVirtualizingPanel)this).ArrangeCompleted?.Invoke();
return result;
}
protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
{
base.ChildrenChanged(sender, e);
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (IControl control in e.NewItems)
{
UpdatePhysicalSizeForAdd(control);
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (IControl control in e.OldItems)
{
UpdatePhysicalSizeForRemove(control);
}
break;
}
}
internal override void ArrangeChild(
IControl child,
Rect rect,
Size panelSize,
Orientation orientation)
{
base.ArrangeChild(child, rect, panelSize, orientation);
if (orientation == Orientation.Horizontal)
{
if (rect.X >= panelSize.Width)
{
++_canBeRemoved;
}
if (rect.Right >= _takenSpace)
{
_takenSpace = rect.Right;
}
}
else
{
if (rect.Y >= panelSize.Height)
{
++_canBeRemoved;
}
if (rect.Bottom >= _takenSpace)
{
_takenSpace = rect.Bottom;
}
}
}
private void UpdatePhysicalSizeForAdd(IControl child)
{
var bounds = Bounds;
var gap = Gap;
child.Measure(bounds.Size);
if (Orientation == Orientation.Vertical)
{
_takenSpace += child.DesiredSize.Height + gap;
}
else
{
_takenSpace += child.DesiredSize.Width + gap;
}
}
private void UpdatePhysicalSizeForRemove(IControl child)
{
var bounds = Bounds;
var gap = Gap;
if (Orientation == Orientation.Vertical)
{
_takenSpace -= child.DesiredSize.Height + gap;
}
else
{
_takenSpace -= child.DesiredSize.Width + gap;
}
}
}
}