ItemContainerGenerator.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. // Copyright (c) The Perspex 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 Perspex.Controls.Templates;
  9. namespace Perspex.Controls.Generators
  10. {
  11. /// <summary>
  12. /// Creates containers for items and maintains a list of created containers.
  13. /// </summary>
  14. public class ItemContainerGenerator : IItemContainerGenerator
  15. {
  16. private Dictionary<int, IControl> _containers = new Dictionary<int, IControl>();
  17. private Subject<ItemContainers> _containersInitialized = new Subject<ItemContainers>();
  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. Owner = owner;
  25. }
  26. /// <summary>
  27. /// Signalled whenever new containers are initialized.
  28. /// </summary>
  29. public IObservable<ItemContainers> ContainersInitialized => _containersInitialized;
  30. /// <summary>
  31. /// Gets the owner control.
  32. /// </summary>
  33. public IControl Owner { get; }
  34. /// <summary>
  35. /// Creates container controls for a collection of items.
  36. /// </summary>
  37. /// <param name="startingIndex">
  38. /// The index of the first item of the data in the containing collection.
  39. /// </param>
  40. /// <param name="items">The items.</param>
  41. /// <param name="itemTemplate">An optional item template.</param>
  42. /// <returns>The created container controls.</returns>
  43. public IList<IControl> CreateContainers(
  44. int startingIndex,
  45. IEnumerable items,
  46. IDataTemplate itemTemplate)
  47. {
  48. Contract.Requires<ArgumentNullException>(items != null);
  49. int index = startingIndex;
  50. var result = new List<IControl>();
  51. foreach (var item in items)
  52. {
  53. IControl container = CreateContainer(item, itemTemplate);
  54. result.Add(container);
  55. }
  56. AddContainers(startingIndex, result);
  57. _containersInitialized.OnNext(new ItemContainers(startingIndex, result));
  58. return result.Where(x => x != null).ToList();
  59. }
  60. /// <summary>
  61. /// Removes a set of created containers from the index and returns the removed controls.
  62. /// </summary>
  63. /// <param name="startingIndex">
  64. /// The index of the first item of the data in the containing collection.
  65. /// </param>
  66. /// <param name="items">The items.</param>
  67. /// <returns>The removed controls.</returns>
  68. public IList<IControl> RemoveContainers(int startingIndex, IEnumerable items)
  69. {
  70. var result = new List<IControl>();
  71. var count = items.Cast<object>().Count();
  72. for (int i = startingIndex; i < startingIndex + count; ++i)
  73. {
  74. var container = _containers[i];
  75. if (container != null)
  76. {
  77. result.Add(container);
  78. _containers.Remove(i);
  79. }
  80. }
  81. return result;
  82. }
  83. /// <summary>
  84. /// Clears the created containers from the index and returns the removed controls.
  85. /// </summary>
  86. /// <returns>The removed controls.</returns>
  87. public IList<IControl> ClearContainers()
  88. {
  89. var result = _containers;
  90. _containers = new Dictionary<int, IControl>();
  91. return result.Values.ToList();
  92. }
  93. /// <summary>
  94. /// Gets the container control representing the item with the specified index.
  95. /// </summary>
  96. /// <param name="index">The index.</param>
  97. /// <returns>The container or null if no container created.</returns>
  98. public IControl ContainerFromIndex(int index)
  99. {
  100. IControl result;
  101. _containers.TryGetValue(index, out result);
  102. return result;
  103. }
  104. /// <summary>
  105. /// Gets the index of the specified container control.
  106. /// </summary>
  107. /// <param name="container">The container.</param>
  108. /// <returns>The index of the container or -1 if not found.</returns>
  109. public int IndexFromContainer(IControl container)
  110. {
  111. foreach (var i in _containers)
  112. {
  113. if (i.Value == container)
  114. {
  115. return i.Key;
  116. }
  117. }
  118. return -1;
  119. }
  120. /// <summary>
  121. /// Creates the container for an item.
  122. /// </summary>
  123. /// <param name="item">The item.</param>
  124. /// <param name="itemTemplate">An optional item template.</param>
  125. /// <returns>The created container control.</returns>
  126. protected virtual IControl CreateContainer(object item, IDataTemplate itemTemplate)
  127. {
  128. if (item == null)
  129. {
  130. return null;
  131. }
  132. else if (itemTemplate != null && itemTemplate.Match(item))
  133. {
  134. var result = itemTemplate.Build(item);
  135. result.DataContext = item;
  136. return result;
  137. }
  138. else
  139. {
  140. return Owner.MaterializeDataTemplate(item);
  141. }
  142. }
  143. /// <summary>
  144. /// Adds a collection of containers to the index.
  145. /// </summary>
  146. /// <param name="index">The starting index.</param>
  147. /// <param name="container">The container.</param>
  148. protected void AddContainers(int index, IList<IControl> container)
  149. {
  150. Contract.Requires<ArgumentNullException>(container != null);
  151. foreach (var c in container)
  152. {
  153. if (!_containers.ContainsKey(index))
  154. {
  155. _containers[index] = c;
  156. }
  157. else
  158. {
  159. throw new InvalidOperationException("Container already created.");
  160. }
  161. ++index;
  162. }
  163. }
  164. }
  165. }