Browse Source

Notify ItemsControl when container index changes.

pull/9677/head
Steven Kirk 4 years ago
parent
commit
a9ca852300
  1. 10
      src/Avalonia.Controls/Generators/ItemContainerGenerator.cs
  2. 16
      src/Avalonia.Controls/ItemsControl.cs
  3. 27
      src/Avalonia.Controls/Presenters/PanelContainerGenerator.cs
  4. 6
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  5. 22
      src/Avalonia.Controls/VirtualizingStackPanel.cs

10
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);
/// <summary>
/// Called when the index for a container changes due to an insertion or removal in the
/// items collection.
/// </summary>
/// <param name="container">The container whose index changed.</param>
/// <param name="oldIndex">The old index.</param>
/// <param name="newIndex">The new index.</param>
public void ItemContainerIndexChanged(Control container, int oldIndex, int newIndex) =>
_owner.ItemContainerIndexChanged(container, oldIndex, newIndex);
/// <summary>
/// Undoes the effects of the <see cref="PrepareItemContainer(Control, object, int)"/> method.
/// </summary>

16
src/Avalonia.Controls/ItemsControl.cs

@ -298,6 +298,17 @@ namespace Avalonia.Controls
container.SetValue(ThemeProperty, ItemContainerTheme, BindingPriority.Template);
}
/// <summary>
/// Called when the index for a container changes due to an insertion or removal in the
/// items collection.
/// </summary>
/// <param name="container">The container whose index changed.</param>
/// <param name="oldIndex">The old index.</param>
/// <param name="newIndex">The new index.</param>
protected virtual void ContainerIndexChangedOverride(Control container, int oldIndex, int newIndex)
{
}
/// <summary>
/// Undoes the effects of the <see cref="PrepareContainerForItemOverride(Control, object?, int)"/> method.
/// </summary>
@ -474,6 +485,11 @@ namespace Avalonia.Controls
PrepareContainerForItemOverride(container, item, index);
}
internal void ItemContainerIndexChanged(Control container, int oldIndex, int newIndex)
{
ContainerIndexChangedOverride(container, oldIndex, newIndex);
}
/// <summary>
/// Given a collection of items, adds those that are controls to the logical children.
/// </summary>

27
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;

6
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);

22
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<Control> _recycleElement;
private readonly Action<Control, int> _updateElementIndex;
private readonly Action<Control, int, int> _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
/// <param name="modelIndex">The index in the source collection of the insert.</param>
/// <param name="count">The number of items inserted.</param>
/// <param name="updateElementIndex">A method used to update the element indexes.</param>
public void ItemsInserted(int modelIndex, int count, Action<Control, int> updateElementIndex)
public void ItemsInserted(int modelIndex, int count, Action<Control, int, int> 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<Control, int> updateElementIndex,
Action<Control, int, int> updateElementIndex,
Action<Control> 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;
}
}
}

Loading…
Cancel
Save