From b91d8781d8d2ca5dcb8971cd2c281511605e7d0b Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 14 May 2016 12:43:56 +0200 Subject: [PATCH] WIP --- .../Views/MainWindow.cs | 11 ++ .../Views/MainWindow.xaml | 11 ++ .../Avalonia.Controls.csproj | 3 + src/Avalonia.Controls/IVirtualizingPanel.cs | 13 ++ src/Avalonia.Controls/Panel.cs | 34 ++-- src/Avalonia.Controls/StackPanel.cs | 18 ++- src/Avalonia.Controls/Thingamybob.cs | 67 ++++++++ .../VirtualizingStackPanel.cs | 148 ++++++++++++++++++ 8 files changed, 284 insertions(+), 21 deletions(-) create mode 100644 src/Avalonia.Controls/IVirtualizingPanel.cs create mode 100644 src/Avalonia.Controls/Thingamybob.cs create mode 100644 src/Avalonia.Controls/VirtualizingStackPanel.cs diff --git a/samples/XamlTestApplicationPcl/Views/MainWindow.cs b/samples/XamlTestApplicationPcl/Views/MainWindow.cs index 2c0012d65f..3df8bdeea3 100644 --- a/samples/XamlTestApplicationPcl/Views/MainWindow.cs +++ b/samples/XamlTestApplicationPcl/Views/MainWindow.cs @@ -25,6 +25,17 @@ namespace XamlTestApplication.Views AvaloniaXamlLoader.Load(this); _exitMenu = this.FindControl("exitMenu"); _exitMenu.Click += (s, e) => Application.Current.Exit(); + + var vadd = this.FindControl + + + + + diff --git a/src/Avalonia.Controls/Avalonia.Controls.csproj b/src/Avalonia.Controls/Avalonia.Controls.csproj index 6f7dd4bcf8..0bc1e5221e 100644 --- a/src/Avalonia.Controls/Avalonia.Controls.csproj +++ b/src/Avalonia.Controls/Avalonia.Controls.csproj @@ -57,6 +57,7 @@ + @@ -161,6 +162,7 @@ + @@ -171,6 +173,7 @@ + diff --git a/src/Avalonia.Controls/IVirtualizingPanel.cs b/src/Avalonia.Controls/IVirtualizingPanel.cs new file mode 100644 index 0000000000..6ac9a83f89 --- /dev/null +++ b/src/Avalonia.Controls/IVirtualizingPanel.cs @@ -0,0 +1,13 @@ +using System; + +namespace Avalonia.Controls +{ + public interface IVirtualizingPanel : IPanel + { + bool IsFull { get; } + + int OverflowCount { get; } + + Action ArrangeCompleted { get; set; } + } +} diff --git a/src/Avalonia.Controls/Panel.cs b/src/Avalonia.Controls/Panel.cs index 55bdbc9dd4..793399841c 100644 --- a/src/Avalonia.Controls/Panel.cs +++ b/src/Avalonia.Controls/Panel.cs @@ -79,12 +79,28 @@ namespace Avalonia.Controls set { SetValue(BackgroundProperty, value); } } + /// + /// Renders the visual to a . + /// + /// The drawing context. + public override void Render(DrawingContext context) + { + var background = Background; + if (background != null) + { + var renderSize = Bounds.Size; + context.FillRectangle(background, new Rect(renderSize)); + } + + base.Render(context); + } + /// /// Called when the collection changes. /// /// The event sender. /// The event args. - private void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) + protected virtual void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e) { List controls; @@ -122,21 +138,5 @@ namespace Avalonia.Controls InvalidateMeasure(); } - - /// - /// Renders the visual to a . - /// - /// The drawing context. - public override void Render(DrawingContext context) - { - var background = Background; - if (background != null) - { - var renderSize = Bounds.Size; - context.FillRectangle(background, new Rect(renderSize)); - } - - base.Render(context); - } } } diff --git a/src/Avalonia.Controls/StackPanel.cs b/src/Avalonia.Controls/StackPanel.cs index 1a032d4def..0284d84df3 100644 --- a/src/Avalonia.Controls/StackPanel.cs +++ b/src/Avalonia.Controls/StackPanel.cs @@ -181,6 +181,7 @@ namespace Avalonia.Controls /// The space taken. protected override Size ArrangeOverride(Size finalSize) { + var orientation = Orientation; double arrangedWidth = finalSize.Width; double arrangedHeight = finalSize.Height; double gap = Gap; @@ -199,11 +200,11 @@ namespace Avalonia.Controls double childWidth = child.DesiredSize.Width; double childHeight = child.DesiredSize.Height; - if (Orientation == Orientation.Vertical) + if (orientation == Orientation.Vertical) { double width = Math.Max(childWidth, arrangedWidth); Rect childFinal = new Rect(0, arrangedHeight, width, childHeight); - child.Arrange(childFinal); + ArrangeChild(child, childFinal, finalSize, orientation); arrangedWidth = Math.Max(arrangedWidth, childWidth); arrangedHeight += childHeight + gap; } @@ -211,13 +212,13 @@ namespace Avalonia.Controls { double height = Math.Max(childHeight, arrangedHeight); Rect childFinal = new Rect(arrangedWidth, 0, childWidth, height); - child.Arrange(childFinal); + ArrangeChild(child, childFinal, finalSize, orientation); arrangedWidth += childWidth + gap; arrangedHeight = Math.Max(arrangedHeight, childHeight); } } - if (Orientation == Orientation.Vertical) + if (orientation == Orientation.Vertical) { arrangedHeight = Math.Max(arrangedHeight - gap, finalSize.Height); } @@ -228,5 +229,14 @@ namespace Avalonia.Controls return new Size(arrangedWidth, arrangedHeight); } + + internal virtual void ArrangeChild( + IControl child, + Rect rect, + Size panelSize, + Orientation orientation) + { + child.Arrange(rect); + } } } \ No newline at end of file diff --git a/src/Avalonia.Controls/Thingamybob.cs b/src/Avalonia.Controls/Thingamybob.cs new file mode 100644 index 0000000000..7fd251e143 --- /dev/null +++ b/src/Avalonia.Controls/Thingamybob.cs @@ -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(); + } + } + } +} diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs new file mode 100644 index 0000000000..8c45148d2b --- /dev/null +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -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; + } + } + } +}