Browse Source

WIP: INCC virtualization support.

pull/554/head
Steven Kirk 10 years ago
parent
commit
3e8a8c6d7c
  1. 4
      samples/VirtualizationTest/MainWindow.xaml
  2. 56
      src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs
  3. 31
      tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

4
samples/VirtualizationTest/MainWindow.xaml

@ -17,6 +17,10 @@
<TextBox Watermark="Viewport"
UseFloatingWatermark="True"
Text="{Binding #listBox.Scroll.Viewport, Mode=OneWay}"/>
<TextBox Watermark="Item to Create"
UseFloatingWatermark="True"
Text="{Binding NewItemString}"/>
<Button Command="{Binding NewItemCommand}">Insert Item</Button>
<Button Command="{Binding RecreateCommand}">Recreate</Button>
</StackPanel>

56
src/Avalonia.Controls/Presenters/ItemVirtualizerSimple.cs

@ -47,7 +47,7 @@ namespace Avalonia.Controls.Presenters
if (delta != 0)
{
RecycleContainers(delta);
RecycleMoveContainers(delta);
FirstIndex += delta;
NextIndex += delta;
}
@ -56,7 +56,7 @@ namespace Avalonia.Controls.Presenters
{
// We're moving to a partially obscured item at the end of the list.
var firstIndex = ItemCount - panel.Children.Count;
RecycleContainers(firstIndex - FirstIndex);
RecycleMoveContainers(firstIndex - FirstIndex);
NextIndex = ItemCount;
FirstIndex = NextIndex - panel.Children.Count;
panel.PixelOffset = VirtualizingPanel.PixelOverflow;
@ -85,14 +85,26 @@ namespace Avalonia.Controls.Presenters
{
base.ItemsChanged(items, e);
if (e.Action == NotifyCollectionChangedAction.Reset)
switch (e.Action)
{
// We could recycle items here if this proves to be inefficient, but
// Reset indicates a large change and should (?) be quite rare.
VirtualizingPanel.Children.Clear();
Owner.ItemContainerGenerator.Clear();
FirstIndex = NextIndex = 0;
CreateRemoveContainers();
case NotifyCollectionChangedAction.Add:
if (e.NewStartingIndex >= FirstIndex &&
e.NewStartingIndex + e.NewItems.Count < NextIndex)
{
CreateRemoveContainers();
RecycleContainers();
}
break;
case NotifyCollectionChangedAction.Reset:
// We could recycle items here if this proves to be inefficient, but
// Reset indicates a large change and should (?) be quite rare.
VirtualizingPanel.Children.Clear();
Owner.ItemContainerGenerator.Clear();
FirstIndex = NextIndex = 0;
CreateRemoveContainers();
break;
}
((ILogicalScrollable)Owner).InvalidateScroll();
@ -161,7 +173,31 @@ namespace Avalonia.Controls.Presenters
}
}
private void RecycleContainers(int delta)
private void RecycleContainers()
{
var panel = VirtualizingPanel;
var generator = Owner.ItemContainerGenerator;
var selector = Owner.MemberSelector;
var containers = generator.Containers.ToList();
var itemIndex = FirstIndex;
foreach (var container in containers)
{
var item = Items.ElementAt(itemIndex);
if (!object.Equals(container.Item, item))
{
if (!generator.TryRecycle(itemIndex, itemIndex, item, selector))
{
throw new NotImplementedException();
}
}
++itemIndex;
}
}
private void RecycleMoveContainers(int delta)
{
var panel = VirtualizingPanel;
var generator = Owner.ItemContainerGenerator;

31
tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization_Simple.cs

@ -2,6 +2,7 @@
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Avalonia.Controls.Generators;
using Avalonia.Controls.Presenters;
@ -154,6 +155,32 @@ namespace Avalonia.Controls.UnitTests.Presenters
Assert.Equal(5, ((IVirtualizingPanel)target.Panel).PixelOffset);
}
[Fact]
public void Inserting_Items_Should_Update_Containers()
{
var target = CreateTarget(itemCount: 20);
target.ApplyTemplate();
target.Measure(new Size(100, 95));
target.Arrange(new Rect(0, 0, 100, 95));
((ILogicalScrollable)target).Offset = new Vector(0, 5);
var expected = Enumerable.Range(5, 10).Select(x => $"Item {x}").ToList();
var items = (ObservableCollection<string>)target.Items;
Assert.Equal(
expected,
target.Panel.Children.Select(x => x.DataContext));
items.Insert(6, "Inserted");
expected.Insert(1, "Inserted");
expected.RemoveAt(expected.Count - 1);
var actual = target.Panel.Children.Select(x => x.DataContext).ToList();
Assert.Equal(expected, actual);
}
public class WithContainers
{
[Fact]
@ -237,7 +264,9 @@ namespace Avalonia.Controls.UnitTests.Presenters
int itemCount = 20)
{
ItemsPresenter result;
var items = Enumerable.Range(0, itemCount).Select(x => $"Item {x}").ToList();
var items = new ObservableCollection<string>(
Enumerable.Range(0, itemCount).Select(x => $"Item {x}"));
var scroller = new ScrollContentPresenter
{

Loading…
Cancel
Save