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