diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
index 0598bb1a63..1cf40d335f 100644
--- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
+++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
@@ -56,6 +56,16 @@ namespace Avalonia.Controls.Generators
public void PrepareItemContainer(Control container, object? item, int index) =>
_owner.PrepareItemContainer(container, item, index);
+ ///
+ /// Called when the index for a container changes due to an insertion or removal in the
+ /// items collection.
+ ///
+ /// The container whose index changed.
+ /// The old index.
+ /// The new index.
+ public void ItemContainerIndexChanged(Control container, int oldIndex, int newIndex) =>
+ _owner.ItemContainerIndexChanged(container, oldIndex, newIndex);
+
///
/// Undoes the effects of the method.
///
diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs
index b7a63811c4..7c24717a06 100644
--- a/src/Avalonia.Controls/ItemsControl.cs
+++ b/src/Avalonia.Controls/ItemsControl.cs
@@ -298,6 +298,17 @@ namespace Avalonia.Controls
container.SetValue(ThemeProperty, ItemContainerTheme, BindingPriority.Template);
}
+ ///
+ /// Called when the index for a container changes due to an insertion or removal in the
+ /// items collection.
+ ///
+ /// The container whose index changed.
+ /// The old index.
+ /// The new index.
+ protected virtual void ContainerIndexChangedOverride(Control container, int oldIndex, int newIndex)
+ {
+ }
+
///
/// Undoes the effects of the method.
///
@@ -474,6 +485,11 @@ namespace Avalonia.Controls
PrepareContainerForItemOverride(container, item, index);
}
+ internal void ItemContainerIndexChanged(Control container, int oldIndex, int newIndex)
+ {
+ ContainerIndexChangedOverride(container, oldIndex, newIndex);
+ }
+
///
/// Given a collection of items, adds those that are controls to the logical children.
///
diff --git a/src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs b/src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs
index 56e1901117..4a31017fbd 100644
--- a/src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs
+++ b/src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs
@@ -63,25 +63,40 @@ namespace Avalonia.Controls.Presenters
return;
var itemsControl = _presenter.ItemsControl;
- var panel = _presenter.Panel;
+ var generator = itemsControl.ItemContainerGenerator;
+ var children = _presenter.Panel.Children;
void Add(int index, IEnumerable items)
{
var i = index;
foreach (var item in items)
{
- panel.Children.Insert(i, CreateContainer(itemsControl, item, i));
+ children.Insert(i, CreateContainer(itemsControl, item, i));
++i;
}
+
+ var childCount = children.Count;
+ var delta = i - index;
+
+ for (; i < childCount; ++i)
+ generator.ItemContainerIndexChanged(children[i], i - delta, i);
}
-
+
void Remove(int index, int count)
{
for (var i = 0; i < count; ++i)
{
- itemsControl.RemoveLogicalChild(panel.Children[i + index]);
- panel.Children.RemoveAt(i + index);
+ var c = children[i];
+ if (!c.IsSet(ItemIsOwnContainerProperty))
+ itemsControl.RemoveLogicalChild(children[i + index]);
}
+
+ children.RemoveRange(index, count);
+
+ var childCount = children.Count;
+
+ for (var i = index; i < childCount; ++i)
+ generator.ItemContainerIndexChanged(children[i], i - count, i);
}
switch (e.Action)
@@ -102,7 +117,7 @@ namespace Avalonia.Controls.Presenters
break;
case NotifyCollectionChangedAction.Reset:
ClearItemsControlLogicalChildren();
- panel.Children.Clear();
+ children.Clear();
if (_presenter.ItemsControl?.Items is { } items)
Add(0, items);
break;
diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
index a49705d754..253726c5aa 100644
--- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
+++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
@@ -449,6 +449,12 @@ namespace Avalonia.Controls.Primitives
}
}
+ protected override void ContainerIndexChangedOverride(Control container, int oldIndex, int newIndex)
+ {
+ base.ContainerIndexChangedOverride(container, oldIndex, newIndex);
+ MarkContainerSelected(container, Selection.IsSelected(newIndex));
+ }
+
protected internal override void ClearContainerForItemOverride(Control element)
{
base.ClearContainerForItemOverride(element);
diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs
index 76cbf94f09..6373d5f3bd 100644
--- a/src/Avalonia.Controls/VirtualizingStackPanel.cs
+++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs
@@ -25,7 +25,7 @@ namespace Avalonia.Controls
private static readonly Rect s_invalidViewport = new(double.PositiveInfinity, double.PositiveInfinity, 0, 0);
private readonly Action _recycleElement;
- private readonly Action _updateElementIndex;
+ private readonly Action _updateElementIndex;
private int _anchorIndex = -1;
private Control? _anchorElement;
private bool _isInLayout;
@@ -513,9 +513,9 @@ namespace Avalonia.Controls
}
}
- private void UpdateElementIndex(Control element, int index)
+ private void UpdateElementIndex(Control element, int oldIndex, int newIndex)
{
- // TODO: Implement this after we refactor ItemContainerGenerator.
+ ItemsControl!.ItemContainerGenerator.ItemContainerIndexChanged(element, oldIndex, newIndex);
}
private void OnEffectiveViewportChanged(object? sender, EffectiveViewportChangedEventArgs e)
@@ -725,7 +725,7 @@ namespace Avalonia.Controls
/// The index in the source collection of the insert.
/// The number of items inserted.
/// A method used to update the element indexes.
- public void ItemsInserted(int modelIndex, int count, Action updateElementIndex)
+ public void ItemsInserted(int modelIndex, int count, Action updateElementIndex)
{
if (modelIndex < 0)
throw new ArgumentOutOfRangeException(nameof(modelIndex));
@@ -742,11 +742,13 @@ namespace Avalonia.Controls
// elements after the insertion point.
var elementCount = _elements.Count;
var start = Math.Max(index, 0);
+ var newIndex = first + count;
for (var i = start; i < elementCount; ++i)
{
if (_elements[i] is Control element)
- updateElementIndex(element, first + i + count);
+ updateElementIndex(element, newIndex - count, newIndex);
+ ++newIndex;
}
if (index <= 0)
@@ -774,7 +776,7 @@ namespace Avalonia.Controls
public void ItemsRemoved(
int modelIndex,
int count,
- Action updateElementIndex,
+ Action updateElementIndex,
Action recycleElement)
{
if (modelIndex < 0)
@@ -794,10 +796,12 @@ namespace Avalonia.Controls
// the indexes of the realized elements.
_firstIndex -= count;
+ var newIndex = _firstIndex;
for (var i = 0; i < _elements.Count; ++i)
{
if (_elements[i] is Control element)
- updateElementIndex(element, _firstIndex + i);
+ updateElementIndex(element, newIndex - count, newIndex);
+ ++newIndex;
}
}
else if (startIndex < _elements.Count)
@@ -822,10 +826,12 @@ namespace Avalonia.Controls
// Update the indexes of the elements after the removed range.
end = _elements.Count;
+ var newIndex = first;
for (var i = start; i < end; ++i)
{
if (_elements[i] is Control element)
- updateElementIndex(element, first + i);
+ updateElementIndex(element, newIndex - count, newIndex);
+ ++newIndex;
}
}
}