PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/V2.2/trunk/CAL/Desktop/Composite.Presentation/Commands/CompositeCommand.cs

#
C# | 258 lines | 148 code | 28 blank | 82 comment | 16 complexity | 549fe08e803314a8f07e7acef921f33c MD5 | raw file
  1. //===================================================================================
  2. // Microsoft patterns & practices
  3. // Composite Application Guidance for Windows Presentation Foundation and Silverlight
  4. //===================================================================================
  5. // Copyright (c) Microsoft Corporation. All rights reserved.
  6. // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
  7. // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
  8. // LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  9. // FITNESS FOR A PARTICULAR PURPOSE.
  10. //===================================================================================
  11. // The example companies, organizations, products, domain names,
  12. // e-mail addresses, logos, people, places, and events depicted
  13. // herein are fictitious. No association with any real company,
  14. // organization, product, domain name, email address, logo, person,
  15. // places, or events is intended or should be inferred.
  16. //===================================================================================
  17. using System;
  18. using System.Collections.Generic;
  19. using System.Linq;
  20. using System.Windows.Input;
  21. using Microsoft.Practices.Composite.Presentation.Properties;
  22. namespace Microsoft.Practices.Composite.Presentation.Commands
  23. {
  24. /// <summary>
  25. /// The CompositeCommand composites one or more ICommands.
  26. /// </summary>
  27. public partial class CompositeCommand : ICommand
  28. {
  29. private readonly List<ICommand> registeredCommands = new List<ICommand>();
  30. private List<WeakReference> _canExecuteChangedHandlers;
  31. private readonly bool monitorCommandActivity;
  32. private readonly EventHandler onRegisteredCommandCanExecuteChangedHandler;
  33. /// <summary>
  34. /// Initializes a new instance of <see cref="CompositeCommand"/>.
  35. /// </summary>
  36. public CompositeCommand()
  37. {
  38. this.onRegisteredCommandCanExecuteChangedHandler = new EventHandler(this.OnRegisteredCommandCanExecuteChanged);
  39. }
  40. /// <summary>
  41. /// Initializes a new instance of <see cref="CompositeCommand"/>.
  42. /// </summary>
  43. /// <param name="monitorCommandActivity">Indicates when the command activity is going to be monitored.</param>
  44. public CompositeCommand(bool monitorCommandActivity)
  45. : this()
  46. {
  47. this.monitorCommandActivity = monitorCommandActivity;
  48. }
  49. /// <summary>
  50. /// Adds a command to the collection and signs up for the <see cref="ICommand.CanExecuteChanged"/> event of it.
  51. /// </summary>
  52. /// <remarks>
  53. /// If this command is set to monitor command activity, and <paramref name="command"/>
  54. /// implements the <see cref="IActiveAware"/> interface, this method will subscribe to its
  55. /// <see cref="IActiveAware.IsActiveChanged"/> event.
  56. /// </remarks>
  57. /// <param name="command">The command to register.</param>
  58. public virtual void RegisterCommand(ICommand command)
  59. {
  60. if (command == this)
  61. {
  62. throw new ArgumentException(Resources.CannotRegisterCompositeCommandInItself);
  63. }
  64. lock (this.registeredCommands)
  65. {
  66. if (this.registeredCommands.Contains(command))
  67. {
  68. throw new InvalidOperationException(Resources.CannotRegisterSameCommandTwice);
  69. }
  70. this.registeredCommands.Add(command);
  71. }
  72. command.CanExecuteChanged += this.onRegisteredCommandCanExecuteChangedHandler;
  73. this.OnCanExecuteChanged();
  74. if (this.monitorCommandActivity)
  75. {
  76. var activeAwareCommand = command as IActiveAware;
  77. if (activeAwareCommand != null)
  78. {
  79. activeAwareCommand.IsActiveChanged += this.Command_IsActiveChanged;
  80. }
  81. }
  82. }
  83. /// <summary>
  84. /// Removes a command from the collection and removes itself from the <see cref="ICommand.CanExecuteChanged"/> event of it.
  85. /// </summary>
  86. /// <param name="command">The command to unregister.</param>
  87. public virtual void UnregisterCommand(ICommand command)
  88. {
  89. bool removed;
  90. lock (this.registeredCommands)
  91. {
  92. removed = this.registeredCommands.Remove(command);
  93. }
  94. if (removed)
  95. {
  96. command.CanExecuteChanged -= this.onRegisteredCommandCanExecuteChangedHandler;
  97. this.OnCanExecuteChanged();
  98. if (this.monitorCommandActivity)
  99. {
  100. var activeAwareCommand = command as IActiveAware;
  101. if (activeAwareCommand != null)
  102. {
  103. activeAwareCommand.IsActiveChanged -= this.Command_IsActiveChanged;
  104. }
  105. }
  106. }
  107. }
  108. private void OnRegisteredCommandCanExecuteChanged(object sender, EventArgs e)
  109. {
  110. this.OnCanExecuteChanged();
  111. }
  112. /// <summary>
  113. /// Forwards <see cref="ICommand.CanExecute"/> to the registered commands and returns
  114. /// <see langword="true" /> if all of the commands return <see langword="true" />.
  115. /// </summary>
  116. /// <param name="parameter">Data used by the command.
  117. /// If the command does not require data to be passed, this object can be set to <see langword="null" />.
  118. /// </param>
  119. /// <returns><see langword="true" /> if all of the commands return <see langword="true" />; otherwise, <see langword="false" />.</returns>
  120. public virtual bool CanExecute(object parameter)
  121. {
  122. bool hasEnabledCommandsThatShouldBeExecuted = false;
  123. ICommand[] commandList;
  124. lock (this.registeredCommands)
  125. {
  126. commandList = this.registeredCommands.ToArray();
  127. }
  128. foreach (ICommand command in commandList)
  129. {
  130. if (this.ShouldExecute(command))
  131. {
  132. if (!command.CanExecute(parameter))
  133. {
  134. return false;
  135. }
  136. hasEnabledCommandsThatShouldBeExecuted = true;
  137. }
  138. }
  139. return hasEnabledCommandsThatShouldBeExecuted;
  140. }
  141. /// <summary>
  142. /// Occurs when any of the registered commands raise <seealso cref="CanExecuteChanged"/>.
  143. /// </summary>
  144. public event EventHandler CanExecuteChanged
  145. {
  146. add
  147. {
  148. WeakEventHandlerManager.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
  149. }
  150. remove
  151. {
  152. WeakEventHandlerManager.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
  153. }
  154. }
  155. /// <summary>
  156. /// Forwards <see cref="ICommand.Execute"/> to the registered commands.
  157. /// </summary>
  158. /// <param name="parameter">Data used by the command.
  159. /// If the command does not require data to be passed, this object can be set to <see langword="null" />.
  160. /// </param>
  161. public virtual void Execute(object parameter)
  162. {
  163. Queue<ICommand> commands;
  164. lock (this.registeredCommands)
  165. {
  166. commands = new Queue<ICommand>(this.registeredCommands.Where(this.ShouldExecute).ToList());
  167. }
  168. while (commands.Count > 0)
  169. {
  170. ICommand command = commands.Dequeue();
  171. command.Execute(parameter);
  172. }
  173. }
  174. /// <summary>
  175. /// Evaluates if a command should execute.
  176. /// </summary>
  177. /// <param name="command">The command to evaluate.</param>
  178. /// <returns>A <see cref="bool"/> value indicating whether the command should be used
  179. /// when evaluating <see cref="CompositeCommand.CanExecute"/> and <see cref="CompositeCommand.Execute"/>.</returns>
  180. /// <remarks>
  181. /// If this command is set to monitor command activity, and <paramref name="command"/>
  182. /// implements the <see cref="IActiveAware"/> interface,
  183. /// this method will return <see langword="false" /> if the command's <see cref="IActiveAware.IsActive"/>
  184. /// property is <see langword="false" />; otherwise it always returns <see langword="true" />.</remarks>
  185. protected virtual bool ShouldExecute(ICommand command)
  186. {
  187. var activeAwareCommand = command as IActiveAware;
  188. if (this.monitorCommandActivity && activeAwareCommand != null)
  189. {
  190. return activeAwareCommand.IsActive;
  191. }
  192. return true;
  193. }
  194. /// <summary>
  195. /// Gets the list of all the registered commands.
  196. /// </summary>
  197. /// <value>A list of registered commands.</value>
  198. /// <remarks>This returns a copy of the commands subscribed to the CompositeCommand.</remarks>
  199. public IList<ICommand> RegisteredCommands
  200. {
  201. get
  202. {
  203. IList<ICommand> commandList;
  204. lock (this.registeredCommands)
  205. {
  206. commandList = this.registeredCommands.ToList();
  207. }
  208. return commandList;
  209. }
  210. }
  211. /// <summary>
  212. /// Raises <see cref="ICommand.CanExecuteChanged"/> on the UI thread so every
  213. /// command invoker can requery <see cref="ICommand.CanExecute"/> to check if the
  214. /// <see cref="CompositeCommand"/> can execute.
  215. /// </summary>
  216. protected virtual void OnCanExecuteChanged()
  217. {
  218. WeakEventHandlerManager.CallWeakReferenceHandlers(this, _canExecuteChangedHandlers);
  219. }
  220. /// <summary>
  221. /// Handler for IsActiveChanged events of registered commands.
  222. /// </summary>
  223. /// <param name="sender">The sender.</param>
  224. /// <param name="e">EventArgs to pass to the event.</param>
  225. private void Command_IsActiveChanged(object sender, EventArgs e)
  226. {
  227. this.OnCanExecuteChanged();
  228. }
  229. }
  230. }