ItemContainerGenerator.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. /// <summary>
  34. /// Gets or sets the data template used to display the items in the control.
  35. /// </summary>
  36. public IDataTemplate ItemTemplate { get; set; }
  37. /// <summary>
  38. /// Gets the owner control.
  39. /// </summary>
  40. public IControl Owner { get; }
  41. /// <inheritdoc/>
  42. public ItemContainerInfo Materialize(
  43. int index,
  44. object item,
  45. IMemberSelector selector)
  46. {
  47. var i = selector != null ? selector.Select(item) : item;
  48. var container = new ItemContainerInfo(CreateContainer(i), item, index);
  49. AddContainer(container);
  50. Materialized?.Invoke(this, new ItemContainerEventArgs(index, container));
  51. return container;
  52. }
  53. /// <inheritdoc/>
  54. public virtual IEnumerable<ItemContainerInfo> Dematerialize(int startingIndex, int count)
  55. {
  56. var result = new List<ItemContainerInfo>();
  57. for (int i = startingIndex; i < startingIndex + count; ++i)
  58. {
  59. if (i < _containers.Count)
  60. {
  61. result.Add(_containers[i]);
  62. _containers[i] = null;
  63. }
  64. }
  65. Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result));
  66. return result;
  67. }
  68. /// <inheritdoc/>
  69. public virtual void InsertSpace(int index, int count)
  70. {
  71. _containers.InsertRange(index, Enumerable.Repeat<ItemContainerInfo>(null, count));
  72. }
  73. /// <inheritdoc/>
  74. public virtual IEnumerable<ItemContainerInfo> RemoveRange(int startingIndex, int count)
  75. {
  76. List<ItemContainerInfo> result = new List<ItemContainerInfo>();
  77. if (startingIndex < _containers.Count)
  78. {
  79. result.AddRange(_containers.GetRange(startingIndex, count));
  80. _containers.RemoveRange(startingIndex, count);
  81. Dematerialized?.Invoke(this, new ItemContainerEventArgs(startingIndex, result));
  82. }
  83. return result;
  84. }
  85. /// <inheritdoc/>
  86. public virtual bool TryRecycle(
  87. int oldIndex,
  88. int newIndex,
  89. object item,
  90. IMemberSelector selector)
  91. {
  92. return false;
  93. }
  94. /// <inheritdoc/>
  95. public virtual IEnumerable<ItemContainerInfo> Clear()
  96. {
  97. var result = _containers.Where(x => x != null).ToList();
  98. _containers = new List<ItemContainerInfo>();
  99. if (result.Count > 0)
  100. {
  101. Dematerialized?.Invoke(this, new ItemContainerEventArgs(0, result));
  102. }
  103. return result;
  104. }
  105. /// <inheritdoc/>
  106. public IControl ContainerFromIndex(int index)
  107. {
  108. if (index < _containers.Count)
  109. {
  110. return _containers[index]?.ContainerControl;
  111. }
  112. return null;
  113. }
  114. /// <inheritdoc/>
  115. public int IndexFromContainer(IControl container)
  116. {
  117. var index = 0;
  118. foreach (var i in _containers)
  119. {
  120. if (i?.ContainerControl == container)
  121. {
  122. return index;
  123. }
  124. ++index;
  125. }
  126. return -1;
  127. }
  128. /// <summary>
  129. /// Creates the container for an item.
  130. /// </summary>
  131. /// <param name="item">The item.</param>
  132. /// <returns>The created container control.</returns>
  133. protected virtual IControl CreateContainer(object item)
  134. {
  135. var result = Owner.MaterializeDataTemplate(item, ItemTemplate);
  136. if (result != null && !(item is IControl))
  137. {
  138. result.DataContext = item;
  139. }
  140. return result;
  141. }
  142. /// <summary>
  143. /// Adds a container to the index.
  144. /// </summary>
  145. /// <param name="container">The container.</param>
  146. protected void AddContainer(ItemContainerInfo container)
  147. {
  148. Contract.Requires<ArgumentNullException>(container != null);
  149. while (_containers.Count < container.Index)
  150. {
  151. _containers.Add(null);
  152. }
  153. if (_containers.Count == container.Index)
  154. {
  155. _containers.Add(container);
  156. }
  157. else if (_containers[container.Index] == null)
  158. {
  159. _containers[container.Index] = container;
  160. }
  161. else
  162. {
  163. throw new InvalidOperationException("Container already created.");
  164. }
  165. }
  166. /// <summary>
  167. /// Moves a container.
  168. /// </summary>
  169. /// <param name="oldIndex">The old index.</param>
  170. /// <param name="newIndex">The new index.</param>
  171. /// <param name="item">The new item.</param>
  172. /// <returns>The container info.</returns>
  173. protected void MoveContainer(int oldIndex, int newIndex, object item)
  174. {
  175. var container = _containers[oldIndex];
  176. var newContainer = new ItemContainerInfo(container.ContainerControl, item, newIndex);
  177. _containers[oldIndex] = null;
  178. AddContainer(newContainer);
  179. }
  180. /// <summary>
  181. /// Gets all containers with an index that fall within a range.
  182. /// </summary>
  183. /// <param name="index">The first index.</param>
  184. /// <param name="count">The number of elements in the range.</param>
  185. /// <returns>The containers.</returns>
  186. protected IEnumerable<ItemContainerInfo> GetContainerRange(int index, int count)
  187. {
  188. return _containers.GetRange(index, count);
  189. }
  190. }
  191. }