8 changed files with 284 additions and 21 deletions
@ -0,0 +1,13 @@ |
|||
using System; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public interface IVirtualizingPanel : IPanel |
|||
{ |
|||
bool IsFull { get; } |
|||
|
|||
int OverflowCount { get; } |
|||
|
|||
Action ArrangeCompleted { get; set; } |
|||
} |
|||
} |
|||
@ -0,0 +1,67 @@ |
|||
using Avalonia.Media; |
|||
using System; |
|||
|
|||
namespace Avalonia.Controls |
|||
{ |
|||
public class Thingamybob : Decorator |
|||
{ |
|||
private int _lastIndex; |
|||
|
|||
public override void ApplyTemplate() |
|||
{ |
|||
if (Child == null) |
|||
{ |
|||
Child = new VirtualizingStackPanel(); |
|||
((IVirtualizingPanel)Child).ArrangeCompleted = CheckPanel; |
|||
} |
|||
} |
|||
|
|||
protected override Size ArrangeOverride(Size finalSize) |
|||
{ |
|||
var result = base.ArrangeOverride(finalSize); |
|||
CreateItems(); |
|||
return result; |
|||
} |
|||
|
|||
private void CreateItems() |
|||
{ |
|||
var panel = Child as IVirtualizingPanel; |
|||
var randomColor = Color.FromUInt32( |
|||
(uint)(0xff000000 + new Random().Next(0xffffff))); |
|||
|
|||
while (!panel.IsFull) |
|||
{ |
|||
panel.Children.Add(new TextBlock |
|||
{ |
|||
Text = "Item " + ++_lastIndex, |
|||
Background = new SolidColorBrush(randomColor), |
|||
}); |
|||
} |
|||
} |
|||
|
|||
private void RemoveItems() |
|||
{ |
|||
var panel = Child as IVirtualizingPanel; |
|||
var remove = panel.OverflowCount; |
|||
|
|||
panel.Children.RemoveRange( |
|||
panel.Children.Count - remove, |
|||
panel.OverflowCount); |
|||
_lastIndex -= remove; |
|||
} |
|||
|
|||
private void CheckPanel() |
|||
{ |
|||
var panel = Child as IVirtualizingPanel; |
|||
|
|||
if (!panel.IsFull) |
|||
{ |
|||
CreateItems(); |
|||
} |
|||
else if (panel.OverflowCount > 0) |
|||
{ |
|||
RemoveItems(); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,148 @@ |
|||
// 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; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue