diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs index bbbe8528b4..fd730c5293 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs @@ -16,7 +16,7 @@ namespace Avalonia.Controls.Generators /// public class ItemContainerGenerator : IItemContainerGenerator { - private List _containers = new List(); + private Dictionary _containers = new Dictionary(); /// /// Initializes a new instance of the class. @@ -30,7 +30,7 @@ namespace Avalonia.Controls.Generators } /// - public IEnumerable Containers => _containers.Where(x => x != null); + public IEnumerable Containers => _containers.Values; /// public event EventHandler Materialized; @@ -60,7 +60,7 @@ namespace Avalonia.Controls.Generators var i = selector != null ? selector.Select(item) : item; var container = new ItemContainerInfo(CreateContainer(i), item, index); - AddContainer(container); + _containers.Add(container.Index, container); Materialized?.Invoke(this, new ItemContainerEventArgs(container)); return container; @@ -73,11 +73,8 @@ namespace Avalonia.Controls.Generators for (int i = startingIndex; i < startingIndex + count; ++i) { - if (i < _containers.Count &&_containers[i] != null) - { - result.Add(_containers[i]); - _containers[i] = null; - } + result.Add(_containers[i]); + _containers.Remove(i); } Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result)); @@ -88,18 +85,47 @@ namespace Avalonia.Controls.Generators /// public virtual void InsertSpace(int index, int count) { - _containers.InsertRange(index, Enumerable.Repeat(null, count)); + if (count > 0) + { + var toMove = _containers.Where(x => x.Key >= index).ToList(); + + foreach (var i in toMove) + { + _containers.Remove(i.Key); + i.Value.Index += count; + _containers[i.Value.Index] = i.Value; + } + } } /// public virtual IEnumerable RemoveRange(int startingIndex, int count) { - List result = new List(); + var result = new List(); - if (startingIndex < _containers.Count) + if (count > 0) { - result.AddRange(_containers.GetRange(startingIndex, count)); - _containers.RemoveRange(startingIndex, count); + for (var i = startingIndex; i < startingIndex + count; ++i) + { + ItemContainerInfo found; + + if (_containers.TryGetValue(i, out found)) + { + result.Add(found); + } + + _containers.Remove(i); + } + + var toMove = _containers.Where(x => x.Key >= startingIndex).ToList(); + + foreach (var i in toMove) + { + _containers.Remove(i.Key); + i.Value.Index -= count; + _containers.Add(i.Value.Index, i.Value); + } + Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result)); } @@ -119,8 +145,8 @@ namespace Avalonia.Controls.Generators /// public virtual IEnumerable Clear() { - var result = _containers.Where(x => x != null).ToList(); - _containers = new List(); + var result = Containers.ToList(); + _containers.Clear(); if (result.Count > 0) { @@ -133,27 +159,20 @@ namespace Avalonia.Controls.Generators /// public IControl ContainerFromIndex(int index) { - if (index < _containers.Count) - { - return _containers[index]?.ContainerControl; - } - - return null; + ItemContainerInfo result; + _containers.TryGetValue(index, out result); + return result?.ContainerControl; } /// public int IndexFromContainer(IControl container) { - var index = 0; - foreach (var i in _containers) { - if (i?.ContainerControl == container) + if (i.Value.ContainerControl == container) { - return index; + return i.Key; } - - ++index; } return -1; @@ -176,33 +195,6 @@ namespace Avalonia.Controls.Generators return result; } - /// - /// Adds a container to the index. - /// - /// The container. - protected void AddContainer(ItemContainerInfo container) - { - Contract.Requires(container != null); - - while (_containers.Count < container.Index) - { - _containers.Add(null); - } - - if (_containers.Count == container.Index) - { - _containers.Add(container); - } - else if (_containers[container.Index] == null) - { - _containers[container.Index] = container; - } - else - { - throw new InvalidOperationException("Container already created."); - } - } - /// /// Moves a container. /// @@ -213,10 +205,11 @@ namespace Avalonia.Controls.Generators protected ItemContainerInfo MoveContainer(int oldIndex, int newIndex, object item) { var container = _containers[oldIndex]; - var newContainer = new ItemContainerInfo(container.ContainerControl, item, newIndex); - _containers[oldIndex] = null; - AddContainer(newContainer); - return newContainer; + container.Index = newIndex; + container.Item = item; + _containers.Remove(oldIndex); + _containers.Add(newIndex, container); + return container; } /// @@ -227,7 +220,7 @@ namespace Avalonia.Controls.Generators /// The containers. protected IEnumerable GetContainerRange(int index, int count) { - return _containers.GetRange(index, count); + return _containers.Where(x => x.Key >= index && x.Key <= index + count).Select(x => x.Value); } /// diff --git a/src/Avalonia.Controls/Generators/ItemContainerInfo.cs b/src/Avalonia.Controls/Generators/ItemContainerInfo.cs index b0fcd2867e..b9387f1022 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerInfo.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerInfo.cs @@ -35,11 +35,11 @@ namespace Avalonia.Controls.Generators /// /// Gets the item that the container represents. /// - public object Item { get; } + public object Item { get; internal set; } /// /// Gets the index of the item in the collection. /// - public int Index { get; } + public int Index { get; internal set; } } } \ No newline at end of file diff --git a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs index ff35d90237..a111515848 100644 --- a/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Generators/ItemContainerGeneratorTests.cs @@ -1,11 +1,9 @@ // 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 System; using System.Collections.Generic; using System.Linq; using Avalonia.Controls.Generators; -using Avalonia.Controls.Templates; using Xunit; namespace Avalonia.Controls.UnitTests.Generators @@ -41,22 +39,6 @@ namespace Avalonia.Controls.UnitTests.Generators Assert.Equal(containers[2].ContainerControl, target.ContainerFromIndex(2)); } - private IList Materialize( - IItemContainerGenerator generator, - int index, - string[] items) - { - var result = new List(); - - foreach (var item in items) - { - var container = generator.Materialize(index++, item, null); - result.Add(container); - } - - return result; - } - [Fact] public void IndexFromContainer_Should_Return_Index() { @@ -98,6 +80,20 @@ namespace Avalonia.Controls.UnitTests.Generators Assert.Equal(expected, result); } + [Fact] + public void InsertSpace_Should_Alter_Successive_Container_Indexes() + { + var items = new[] { "foo", "bar", "baz" }; + var owner = new Decorator(); + var target = new ItemContainerGenerator(owner); + var containers = Materialize(target, 0, items); + + target.InsertSpace(1, 3); + + Assert.Equal(3, target.Containers.Count()); + Assert.Equal(new[] { 0, 4, 5 }, target.Containers.Select(x => x.Index)); + } + [Fact] public void RemoveRange_Should_Alter_Successive_Container_Indexes() { @@ -111,6 +107,23 @@ namespace Avalonia.Controls.UnitTests.Generators Assert.Equal(containers[0].ContainerControl, target.ContainerFromIndex(0)); Assert.Equal(containers[2].ContainerControl, target.ContainerFromIndex(1)); Assert.Equal(containers[1], removed); + Assert.Equal(new[] { 0, 1 }, target.Containers.Select(x => x.Index)); + } + + private IList Materialize( + IItemContainerGenerator generator, + int index, + string[] items) + { + var result = new List(); + + foreach (var item in items) + { + var container = generator.Materialize(index++, item, null); + result.Add(container); + } + + return result; } } } diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs b/tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs index 33bbf83140..cdc2fa65a5 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/CarouselPresenterTests.cs @@ -155,7 +155,7 @@ namespace Avalonia.Controls.UnitTests.Presenters } [Fact] - public void Should_have_correct_index_itemscontainer() + public void Should_Have_Correct_ItemsContainer_Index() { ObservableCollection items = new ObservableCollection(); @@ -186,7 +186,7 @@ namespace Avalonia.Controls.UnitTests.Presenters items.Remove(items[0]); Assert.Equal(1, target.ItemContainerGenerator.Containers.Count()); Assert.Equal(1, target.Panel.Children.Count); - Assert.Equal(1, target.ItemContainerGenerator.Containers.First().Index); + Assert.Equal(0, target.ItemContainerGenerator.Containers.First().Index); items.Remove(items[0]); Assert.Equal(0, target.ItemContainerGenerator.Containers.Count());