using System;
using System.Collections.Generic;
using System.Linq;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Templates;
using Avalonia.Data;
namespace Avalonia.Controls.Generators
{
///
/// Creates containers for items and maintains a list of created containers.
///
public class ItemContainerGenerator : IItemContainerGenerator
{
private SortedDictionary _containers = new SortedDictionary();
///
/// Initializes a new instance of the class.
///
/// The owner control.
public ItemContainerGenerator(IControl owner)
{
Owner = owner ?? throw new ArgumentNullException(nameof(owner));
}
///
public IEnumerable Containers => _containers.Values;
///
public event EventHandler? Materialized;
///
public event EventHandler? Dematerialized;
///
public event EventHandler? Recycled;
///
/// Gets or sets the data template used to display the items in the control.
///
public IDataTemplate? ItemTemplate { get; set; }
///
/// Gets the owner control.
///
public IControl Owner { get; }
///
public virtual Type? ContainerType => null;
///
public ItemContainerInfo Materialize(int index, object item)
{
var container = new ItemContainerInfo(CreateContainer(item)!, item, index);
_containers.Add(container.Index, container);
Materialized?.Invoke(this, new ItemContainerEventArgs(container));
return container;
}
///
public virtual IEnumerable Dematerialize(int startingIndex, int count)
{
var result = new List();
for (int i = startingIndex; i < startingIndex + count; ++i)
{
result.Add(_containers[i]);
_containers.Remove(i);
}
Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result));
return result;
}
///
public virtual void InsertSpace(int index, int count)
{
if (count > 0)
{
var toMove = _containers.Where(x => x.Key >= index)
.OrderByDescending(x => x.Key)
.ToArray();
foreach (var i in toMove)
{
_containers.Remove(i.Key);
i.Value.Index += count;
_containers.Add(i.Value.Index, i.Value);
}
}
}
///
public virtual IEnumerable RemoveRange(int startingIndex, int count)
{
var result = new List();
if (count > 0)
{
for (var i = startingIndex; i < startingIndex + count; ++i)
{
if (_containers.TryGetValue(i, out var found))
{
result.Add(found);
}
_containers.Remove(i);
}
var toMove = _containers.Where(x => x.Key >= startingIndex)
.OrderBy(x => x.Key).ToArray();
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));
if (toMove.Length > 0)
{
var containers = toMove.Select(x => x.Value).ToArray();
Recycled?.Invoke(this, new ItemContainerEventArgs(containers[0].Index, containers));
}
}
return result;
}
///
public virtual bool TryRecycle(int oldIndex, int newIndex, object item) => false;
///
public virtual IEnumerable Clear()
{
var result = Containers.ToArray();
_containers.Clear();
if (result.Length > 0)
{
Dematerialized?.Invoke(this, new ItemContainerEventArgs(0, result));
}
return result;
}
///
public IControl? ContainerFromIndex(int index)
{
ItemContainerInfo? result;
_containers.TryGetValue(index, out result);
return result?.ContainerControl;
}
///
public int IndexFromContainer(IControl? container)
{
foreach (var i in _containers)
{
if (i.Value.ContainerControl == container)
{
return i.Key;
}
}
return -1;
}
///
/// Creates the container for an item.
///
/// The item.
/// The created container control.
protected virtual IControl? CreateContainer(object item)
{
var result = item as IControl;
if (result == null)
{
result = new ContentPresenter();
result.SetValue(ContentPresenter.ContentProperty, item, BindingPriority.Style);
if (ItemTemplate != null)
{
result.SetValue(
ContentPresenter.ContentTemplateProperty,
ItemTemplate,
BindingPriority.TemplatedParent);
}
}
return result;
}
///
/// Moves a container.
///
/// The old index.
/// The new index.
/// The new item.
/// The container info.
protected ItemContainerInfo MoveContainer(int oldIndex, int newIndex, object item)
{
var container = _containers[oldIndex];
container.Index = newIndex;
container.Item = item;
_containers.Remove(oldIndex);
_containers.Add(newIndex, container);
return container;
}
///
/// Gets all containers with an index that fall within a range.
///
/// The first index.
/// The number of elements in the range.
/// The containers.
protected IEnumerable GetContainerRange(int index, int count)
{
return _containers.Where(x => x.Key >= index && x.Key < index + count).Select(x => x.Value);
}
///
/// Raises the event.
///
/// The event args.
protected void RaiseRecycled(ItemContainerEventArgs e)
{
Recycled?.Invoke(this, e);
}
}
}