ItemContainerGenerator.cs 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. // Copyright (c) The Avalonia Project. All rights reserved.
  2. // Licensed under the MIT license. See licence.md file in the project root for full license information.
  3. using System;
  4. using System.Collections;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Reactive.Subjects;
  8. using Avalonia.Controls.Templates;
  9. using Avalonia.Controls.Utils;
  10. namespace Avalonia.Controls.Generators
  11. {
  12. /// <summary>
  13. /// Creates containers for items and maintains a list of created containers.
  14. /// </summary>
  15. public class ItemContainerGenerator : IItemContainerGenerator
  16. {
  17. private List<ItemContainerInfo> _containers = new List<ItemContainerInfo>();
  18. /// <summary>
  19. /// Initializes a new instance of the <see cref="ItemContainerGenerator"/> class.
  20. /// </summary>
  21. /// <param name="owner">The owner control.</param>
  22. public ItemContainerGenerator(IControl owner)
  23. {
  24. Contract.Requires<ArgumentNullException>(owner != null);
  25. Owner = owner;
  26. }
  27. /// <inheritdoc/>
  28. public IEnumerable<ItemContainerInfo> Containers => _containers.Where(x => x != null);
  29. /// <inheritdoc/>
  30. public event EventHandler<ItemContainerEventArgs> Materialized;
  31. /// <inheritdoc/>
  32. public event EventHandler<ItemContainerEventArgs> Dematerialized;
  33. /// <inheritdoc/>
  34. public event EventHandler<ItemContainerEventArgs> Recycled;
  35. /// <summary>
  36. /// Gets or sets the data template used to display the items in the control.
  37. /// </summary>
  38. public IDataTemplate ItemTemplate { get; set; }
  39. /// <summary>
  40. /// Gets the owner control.
  41. /// </summary>
  42. public IControl Owner { get; }
  43. /// <inheritdoc/>
  44. public ItemContainerInfo Materialize(
  45. int index,
  46. object item,
  47. IMemberSelector selector)
  48. {
  49. var i = selector != null ? selector.Select(item) : item;
  50. var container = new ItemContainerInfo(CreateContainer(i), item, index);
  51. AddContainer(container);
  52. Materialized?.Invoke(this, new ItemContainerEventArgs(container));
  53. return container;
  54. }
  55. /// <inheritdoc/>
  56. public virtual IEnumerable<ItemContainerInfo> Dematerialize(int startingIndex, int count)
  57. {
  58. var result = new List<ItemContainerInfo>();
  59. for (int i = startingIndex; i < startingIndex + count; ++i)
  60. {
  61. if (i < _containers.Count)
  62. {
  63. result.Add(_containers[i]);
  64. _containers[i] = null;
  65. }
  66. }
  67. Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result));
  68. return result;
  69. }
  70. /// <inheritdoc/>
  71. public virtual void InsertSpace(int index, int count)
  72. {
  73. _containers.InsertRange(index, Enumerable.Repeat<ItemContainerInfo>(null, count));
  74. }
  75. /// <inheritdoc/>
  76. public virtual IEnumerable<ItemContainerInfo> RemoveRange(int startingIndex, int count)
  77. {
  78. List<ItemContainerInfo> result = new List<ItemContainerInfo>();
  79. if (startingIndex < _containers.Count)
  80. {
  81. result.AddRange(_containers.GetRange(startingIndex, count));
  82. _containers.RemoveRange(startingIndex, count);
  83. Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result));
  84. }
  85. return result;
  86. }
  87. /// <inheritdoc/>
  88. public virtual bool TryRecycle(
  89. int oldIndex,
  90. int newIndex,
  91. object item,
  92. IMemberSelector selector)
  93. {
  94. return false;
  95. }
  96. /// <inheritdoc/>
  97. public virtual IEnumerable<ItemContainerInfo> Clear()
  98. {
  99. var result = _containers.Where(x => x != null).ToList();
  100. _containers = new List<ItemContainerInfo>();
  101. if (result.Count > 0)
  102. {
  103. Dematerialized?.Invoke(this, new ItemContainerEventArgs(0, result));
  104. }
  105. return result;
  106. }
  107. /// <inheritdoc/>
  108. public IControl ContainerFromIndex(int index)
  109. {
  110. if (index < _containers.Count)
  111. {
  112. return _containers[index]?.ContainerControl;
  113. }
  114. return null;
  115. }
  116. /// <inheritdoc/>
  117. public int IndexFromContainer(IControl container)
  118. {
  119. var index = 0;
  120. foreach (var i in _containers)
  121. {
  122. if (i?.ContainerControl == container)
  123. {
  124. return index;
  125. }
  126. ++index;
  127. }
  128. return -1;
  129. }
  130. /// <summary>
  131. /// Creates the container for an item.
  132. /// </summary>
  133. /// <param name="item">The item.</param>
  134. /// <returns>The created container control.</returns>
  135. protected virtual IControl CreateContainer(object item)
  136. {
  137. var result = Owner.MaterializeDataTemplate(item, ItemTemplate);
  138. if (result != null && !(item is IControl))
  139. {
  140. result.DataContext = item;
  141. }
  142. return result;
  143. }
  144. /// <summary>
  145. /// Adds a container to the index.
  146. /// </summary>
  147. /// <param name="container">The container.</param>
  148. protected void AddContainer(ItemContainerInfo container)
  149. {
  150. Contract.Requires<ArgumentNullException>(container != null);
  151. while (_containers.Count < container.Index)
  152. {
  153. _containers.Add(null);
  154. }
  155. if (_containers.Count == container.Index)
  156. {
  157. _containers.Add(container);
  158. }
  159. else if (_containers[container.Index] == null)
  160. {
  161. _containers[container.Index] = container;
  162. }
  163. else
  164. {
  165. throw new InvalidOperationException("Container already created.");
  166. }
  167. }
  168. /// <summary>
  169. /// Moves a container.
  170. /// </summary>
  171. /// <param name="oldIndex">The old index.</param>
  172. /// <param name="newIndex">The new index.</param>
  173. /// <param name="item">The new item.</param>
  174. /// <returns>The container info.</returns>
  175. protected ItemContainerInfo MoveContainer(int oldIndex, int newIndex, object item)
  176. {
  177. var container = _containers[oldIndex];
  178. var newContainer = new ItemContainerInfo(container.ContainerControl, item, newIndex);
  179. _containers[oldIndex] = null;
  180. AddContainer(newContainer);
  181. return newContainer;
  182. }
  183. /// <summary>
  184. /// Gets all containers with an index that fall within a range.
  185. /// </summary>
  186. /// <param name="index">The first index.</param>
  187. /// <param name="count">The number of elements in the range.</param>
  188. /// <returns>The containers.</returns>
  189. protected IEnumerable<ItemContainerInfo> GetContainerRange(int index, int count)
  190. {
  191. return _containers.GetRange(index, count);
  192. }
  193. /// <summary>
  194. /// Raises the <see cref="Recycled"/> event.
  195. /// </summary>
  196. /// <param name="e">The event args.</param>
  197. protected void RaiseRecycled(ItemContainerEventArgs e)
  198. {
  199. Recycled?.Invoke(this, e);
  200. }
  201. }
  202. }