ConductorWithCollectionOneActive.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. namespace Caliburn.Micro.Core {
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Collections.Specialized;
  5. using System.Linq;
  6. public partial class Conductor<T> {
  7. /// <summary>
  8. /// An implementation of <see cref="IConductor"/> that holds on many items.
  9. /// </summary>
  10. public partial class Collection {
  11. /// <summary>
  12. /// An implementation of <see cref="IConductor"/> that holds on many items but only activates one at a time.
  13. /// </summary>
  14. public class OneActive : ConductorBaseWithActiveItem<T> {
  15. readonly BindableCollection<T> items = new BindableCollection<T>();
  16. /// <summary>
  17. /// Initializes a new instance of the <see cref="Conductor&lt;T&gt;.Collection.OneActive"/> class.
  18. /// </summary>
  19. public OneActive() {
  20. items.CollectionChanged += (s, e) => {
  21. switch(e.Action) {
  22. case NotifyCollectionChangedAction.Add:
  23. e.NewItems.OfType<IChild>().Apply(x => x.Parent = this);
  24. break;
  25. case NotifyCollectionChangedAction.Remove:
  26. e.OldItems.OfType<IChild>().Apply(x => x.Parent = null);
  27. break;
  28. case NotifyCollectionChangedAction.Replace:
  29. e.NewItems.OfType<IChild>().Apply(x => x.Parent = this);
  30. e.OldItems.OfType<IChild>().Apply(x => x.Parent = null);
  31. break;
  32. case NotifyCollectionChangedAction.Reset:
  33. items.OfType<IChild>().Apply(x => x.Parent = this);
  34. break;
  35. }
  36. };
  37. }
  38. /// <summary>
  39. /// Gets the items that are currently being conducted.
  40. /// </summary>
  41. public IObservableCollection<T> Items {
  42. get { return items; }
  43. }
  44. /// <summary>
  45. /// Gets the children.
  46. /// </summary>
  47. /// <returns>The collection of children.</returns>
  48. public override IEnumerable<T> GetChildren() {
  49. return items;
  50. }
  51. /// <summary>
  52. /// Activates the specified item.
  53. /// </summary>
  54. /// <param name="item">The item to activate.</param>
  55. public override void ActivateItem(T item) {
  56. if(item != null && item.Equals(ActiveItem)) {
  57. if (IsActive) {
  58. ScreenExtensions.TryActivate(item);
  59. OnActivationProcessed(item, true);
  60. }
  61. return;
  62. }
  63. ChangeActiveItem(item, false);
  64. }
  65. /// <summary>
  66. /// Deactivates the specified item.
  67. /// </summary>
  68. /// <param name="item">The item to close.</param>
  69. /// <param name="close">Indicates whether or not to close the item after deactivating it.</param>
  70. public override void DeactivateItem(T item, bool close) {
  71. if (item == null) {
  72. return;
  73. }
  74. if (!close) {
  75. ScreenExtensions.TryDeactivate(item, false);
  76. }
  77. else {
  78. CloseStrategy.Execute(new[] { item }, (canClose, closable) => {
  79. if (canClose) {
  80. CloseItemCore(item);
  81. }
  82. });
  83. }
  84. }
  85. void CloseItemCore(T item) {
  86. if(item.Equals(ActiveItem)) {
  87. var index = items.IndexOf(item);
  88. var next = DetermineNextItemToActivate(items, index);
  89. ChangeActiveItem(next, true);
  90. }
  91. else {
  92. ScreenExtensions.TryDeactivate(item, true);
  93. }
  94. items.Remove(item);
  95. }
  96. /// <summary>
  97. /// Determines the next item to activate based on the last active index.
  98. /// </summary>
  99. /// <param name="list">The list of possible active items.</param>
  100. /// <param name="lastIndex">The index of the last active item.</param>
  101. /// <returns>The next item to activate.</returns>
  102. /// <remarks>Called after an active item is closed.</remarks>
  103. protected virtual T DetermineNextItemToActivate(IList<T> list, int lastIndex) {
  104. var toRemoveAt = lastIndex - 1;
  105. if (toRemoveAt == -1 && list.Count > 1) {
  106. return list[1];
  107. }
  108. if (toRemoveAt > -1 && toRemoveAt < list.Count - 1) {
  109. return list[toRemoveAt];
  110. }
  111. return default(T);
  112. }
  113. /// <summary>
  114. /// Called to check whether or not this instance can close.
  115. /// </summary>
  116. /// <param name="callback">The implementor calls this action with the result of the close check.</param>
  117. public override void CanClose(Action<bool> callback) {
  118. CloseStrategy.Execute(items.ToList(), (canClose, closable) => {
  119. if(!canClose && closable.Any()) {
  120. if(closable.Contains(ActiveItem)) {
  121. var list = items.ToList();
  122. var next = ActiveItem;
  123. do {
  124. var previous = next;
  125. next = DetermineNextItemToActivate(list, list.IndexOf(previous));
  126. list.Remove(previous);
  127. } while(closable.Contains(next));
  128. var previousActive = ActiveItem;
  129. ChangeActiveItem(next, true);
  130. items.Remove(previousActive);
  131. var stillToClose = closable.ToList();
  132. stillToClose.Remove(previousActive);
  133. closable = stillToClose;
  134. }
  135. closable.OfType<IDeactivate>().Apply(x => x.Deactivate(true));
  136. items.RemoveRange(closable);
  137. }
  138. callback(canClose);
  139. });
  140. }
  141. /// <summary>
  142. /// Called when activating.
  143. /// </summary>
  144. protected override void OnActivate() {
  145. ScreenExtensions.TryActivate(ActiveItem);
  146. }
  147. /// <summary>
  148. /// Called when deactivating.
  149. /// </summary>
  150. /// <param name="close">Inidicates whether this instance will be closed.</param>
  151. protected override void OnDeactivate(bool close) {
  152. if (close) {
  153. items.OfType<IDeactivate>().Apply(x => x.Deactivate(true));
  154. items.Clear();
  155. }
  156. else {
  157. ScreenExtensions.TryDeactivate(ActiveItem, false);
  158. }
  159. }
  160. /// <summary>
  161. /// Ensures that an item is ready to be activated.
  162. /// </summary>
  163. /// <param name="newItem">The item that is about to be activated.</param>
  164. /// <returns>The item to be activated.</returns>
  165. protected override T EnsureItem(T newItem) {
  166. if (newItem == null) {
  167. newItem = DetermineNextItemToActivate(items, ActiveItem != null ? items.IndexOf(ActiveItem) : 0);
  168. }
  169. else {
  170. var index = items.IndexOf(newItem);
  171. if (index == -1)
  172. items.Add(newItem);
  173. else newItem = items[index];
  174. }
  175. return base.EnsureItem(newItem);
  176. }
  177. public void ClearItems()
  178. {
  179. items.Clear();
  180. ChangeActiveItem(null, true);
  181. }
  182. }
  183. }
  184. }
  185. }