PageRenderTime 29ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Caliburn.PresentationFramework/Commands/CommandMessage.wpf.cs

http://caliburn.codeplex.com
C# | 405 lines | 236 code | 57 blank | 112 comment | 21 complexity | 722fd578b7e70d539474ccf44c4bde56 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause
  1. #if !SILVERLIGHT
  2. namespace Caliburn.PresentationFramework.Commands
  3. {
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Windows;
  8. using System.Windows.Markup;
  9. using Actions;
  10. using Configuration;
  11. using Core;
  12. using Core.InversionOfControl;
  13. using Core.Logging;
  14. using RoutedMessaging;
  15. using ViewModels;
  16. using Action=System.Action;
  17. /// <summary>
  18. /// An <see cref="IRoutedMessage"/> for commands.
  19. /// </summary>
  20. [ContentProperty("Parameters")]
  21. public class CommandMessage : Freezable, IRoutedMessageWithOutcome, IRoutedMessageHandler
  22. {
  23. static readonly ILog Log = LogManager.GetLog(typeof(CommandMessage));
  24. /// <summary>
  25. /// Represents the parameters of a command message.
  26. /// </summary>
  27. public static readonly DependencyProperty ParametersProperty =
  28. DependencyProperty.Register(
  29. "Parameters",
  30. typeof(FreezableCollection<Parameter>),
  31. typeof(CommandMessage)
  32. );
  33. /// <summary>
  34. /// Represents the command tied to the message.
  35. /// </summary>
  36. public static readonly DependencyProperty CommandProperty =
  37. DependencyProperty.Register(
  38. "Command",
  39. typeof(object),
  40. typeof(CommandMessage),
  41. new PropertyMetadata(CommandChanged)
  42. );
  43. /// <summary>
  44. /// Represents the parent of the command.
  45. /// </summary>
  46. public static readonly DependencyProperty ParentCommandProperty =
  47. DependencyProperty.Register(
  48. "ParentCommand",
  49. typeof(ICompositeCommand),
  50. typeof(CommandMessage)
  51. );
  52. /// <summary>
  53. /// Represents the return path of an command message.
  54. /// </summary>
  55. public static readonly DependencyProperty OutcomePathProperty =
  56. DependencyProperty.Register(
  57. "OutcomePath",
  58. typeof(string),
  59. typeof(CommandMessage)
  60. );
  61. /// <summary>
  62. /// Represents the availability effect of an command message.
  63. /// </summary>
  64. public static readonly DependencyProperty AvailabilityEffectProperty =
  65. DependencyProperty.Register(
  66. "AvailabilityEffect",
  67. typeof(IAvailabilityEffect),
  68. typeof(CommandMessage)
  69. );
  70. static void CommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
  71. {
  72. if(e.NewValue != e.OldValue && e.NewValue != null)
  73. {
  74. var message = (CommandMessage)d;
  75. message.CreateAction();
  76. }
  77. }
  78. IInteractionNode source;
  79. readonly IViewModelDescriptionFactory factory;
  80. ActionMessage actionMessage;
  81. IAction action;
  82. IList<object> metadata;
  83. /// <summary>
  84. /// Initializes a new instance of the <see cref="CommandMessage"/> class.
  85. /// </summary>
  86. public CommandMessage()
  87. {
  88. if(!PresentationFrameworkConfiguration.IsInDesignMode)
  89. factory = IoC.Get<IViewModelDescriptionFactory>();
  90. SetValue(ParametersProperty, new FreezableCollection<Parameter>());
  91. }
  92. /// <summary>
  93. /// Gets the metadata.
  94. /// </summary>
  95. /// <value>The metadata.</value>
  96. public IList<object> Metadata
  97. {
  98. get
  99. {
  100. if(metadata == null)
  101. metadata = new List<object>();
  102. return metadata;
  103. }
  104. }
  105. /// <summary>
  106. /// Gets or sets the command.
  107. /// </summary>
  108. /// <value>The command.</value>
  109. public object Command
  110. {
  111. get { return GetValue(CommandProperty); }
  112. set { SetValue(CommandProperty, value); }
  113. }
  114. /// <summary>
  115. /// Gets or sets the parent.
  116. /// </summary>
  117. /// <value>The parent.</value>
  118. public ICompositeCommand ParentCommand
  119. {
  120. get { return (ICompositeCommand)GetValue(ParentCommandProperty); }
  121. set { SetValue(ParentCommandProperty, value); }
  122. }
  123. /// <summary>
  124. /// Occurs when the command has completed executing.
  125. /// </summary>
  126. public event EventHandler Completed = delegate { };
  127. void OnCompleted()
  128. {
  129. Completed(this, EventArgs.Empty);
  130. }
  131. /// <summary>
  132. /// Gets or sets the path to use in binding the return value.
  133. /// </summary>
  134. /// <value>The return path.</value>
  135. public string OutcomePath
  136. {
  137. get { return (string)GetValue(OutcomePathProperty); }
  138. set { SetValue(OutcomePathProperty, value); }
  139. }
  140. /// <summary>
  141. /// Gets the default element to bind to if no return path is specified.
  142. /// </summary>
  143. /// <value>The default element.</value>
  144. public string DefaultOutcomeElement
  145. {
  146. get { return actionMessage.DefaultOutcomeElement; }
  147. }
  148. /// <summary>
  149. /// Gets or sets the availability effect.
  150. /// </summary>
  151. /// <value>The availability effect.</value>
  152. public IAvailabilityEffect AvailabilityEffect
  153. {
  154. get { return (IAvailabilityEffect)GetValue(AvailabilityEffectProperty); }
  155. set { SetValue(AvailabilityEffectProperty, value); }
  156. }
  157. /// <summary>
  158. /// Gets the parameters.
  159. /// </summary>
  160. /// <value>The parameters.</value>
  161. public FreezableCollection<Parameter> Parameters
  162. {
  163. get { return (FreezableCollection<Parameter>)GetValue(ParametersProperty); }
  164. }
  165. /// <summary>
  166. /// Gets the source of the message.
  167. /// </summary>
  168. /// <value>The source.</value>
  169. public IInteractionNode Source
  170. {
  171. get { return source; }
  172. }
  173. /// <summary>
  174. /// Initializes the message for interaction with the specified node.
  175. /// </summary>
  176. /// <param name="node">The node.</param>
  177. public void Initialize(IInteractionNode node)
  178. {
  179. source = node;
  180. foreach(var parameter in Parameters)
  181. {
  182. parameter.ValueChanged += () => Invalidated();
  183. }
  184. }
  185. /// <summary>
  186. /// Occurs when the message is invalidated.
  187. /// </summary>
  188. public event Action Invalidated = delegate { };
  189. /// <summary>
  190. /// When implemented in a derived class, creates a new instance of the <see cref="T:System.Windows.Freezable"/> derived class.
  191. /// </summary>
  192. /// <returns>The new instance.</returns>
  193. protected override Freezable CreateInstanceCore()
  194. {
  195. return new CommandMessage();
  196. }
  197. /// <summary>
  198. /// Gets the underlying object instance to which this handler routes requests.
  199. /// </summary>
  200. /// <returns></returns>
  201. public object Unwrap()
  202. {
  203. return Command;
  204. }
  205. /// <summary>
  206. /// Indicates whether this instance can handle the speicified message.
  207. /// </summary>
  208. /// <param name="message">The message.</param>
  209. /// <returns></returns>
  210. public bool Handles(IRoutedMessage message)
  211. {
  212. return message == this;
  213. }
  214. /// <summary>
  215. /// Processes the specified message.
  216. /// </summary>
  217. /// <param name="message">The message.</param>
  218. /// <param name="context">An object that provides additional context for message processing.</param>
  219. public void Process(IRoutedMessage message, object context)
  220. {
  221. if (message != this)
  222. {
  223. var ex = new CaliburnException("The handler cannot process the message.");
  224. Log.Error(ex);
  225. throw ex;
  226. }
  227. CreateActionMessage();
  228. action.Execute(actionMessage, Source, context);
  229. Log.Info("Processed {0} on command {1}.", actionMessage, Command);
  230. }
  231. /// <summary>
  232. /// Updates the availability of the trigger.
  233. /// </summary>
  234. /// <param name="trigger">The trigger.</param>
  235. public void UpdateAvailability(IMessageTrigger trigger)
  236. {
  237. if(trigger.Message != this)
  238. {
  239. var ex = new CaliburnException("The handler cannot update availability for this trigger.");
  240. Log.Error(ex);
  241. throw ex;
  242. }
  243. if (Command != null)
  244. {
  245. CreateActionMessage();
  246. bool isAvailable = action.ShouldTriggerBeAvailable(actionMessage, Source);
  247. trigger.UpdateAvailabilty(isAvailable);
  248. TryUpdateParentAvailability(isAvailable);
  249. }
  250. }
  251. /// <summary>
  252. /// Makes the handler aware of a specific trigger.
  253. /// </summary>
  254. /// <param name="trigger">The trigger.</param>
  255. public void MakeAwareOf(IMessageTrigger trigger)
  256. {
  257. if(trigger.Message != this)
  258. return;
  259. CreateActionMessage();
  260. bool isAvailable = action.ShouldTriggerBeAvailable(actionMessage, Source);
  261. trigger.UpdateAvailabilty(isAvailable);
  262. TryUpdateParentAvailability(isAvailable);
  263. action.Filters.HandlerAware.Apply(x => x.MakeAwareOf(this, trigger));
  264. Log.Info("Made handler aware of filters for {0}.", Command);
  265. }
  266. /// <summary>
  267. /// Indicates whether this message is related to the potential target.
  268. /// </summary>
  269. /// <param name="potentialTarget">The potential target.</param>
  270. /// <returns></returns>
  271. public bool RelatesTo(object potentialTarget)
  272. {
  273. return actionMessage.RelatesTo(potentialTarget);
  274. }
  275. /// <summary>
  276. /// Indicates whether the current object is equal to another object of the same type.
  277. /// </summary>
  278. /// <param name="other">An object to compare with this object.</param>
  279. /// <returns>
  280. /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
  281. /// </returns>
  282. public bool Equals(IRoutedMessage other)
  283. {
  284. if(other is ActionMessage)
  285. return ReferenceEquals(actionMessage, other);
  286. return ReferenceEquals(this, other);
  287. }
  288. void TryUpdateParentAvailability(bool isAvailable)
  289. {
  290. var parent = ParentCommand ?? Commands.Command.GetParent(Source.UIElement);
  291. if(parent != null)
  292. {
  293. parent.AddOrUpdateChild(this, isAvailable);
  294. Log.Info("Updating parent {0} availability for {1}.", parent, Command);
  295. }
  296. }
  297. void CreateActionMessage()
  298. {
  299. string methodName = "Execute";
  300. var att = Command.GetType()
  301. .GetAttributes<CommandAttribute>(true)
  302. .FirstOrDefault();
  303. if(att != null)
  304. methodName = att.ExecuteMethod;
  305. actionMessage = new ActionMessage
  306. {
  307. MethodName = methodName,
  308. AvailabilityEffect = AvailabilityEffect,
  309. OutcomePath = OutcomePath
  310. };
  311. actionMessage.Initialize(Source);
  312. foreach(var parameter in Parameters)
  313. {
  314. actionMessage.Parameters.Add(new Parameter(parameter.Value));
  315. }
  316. }
  317. void CreateAction()
  318. {
  319. var host = factory.Create(Command.GetType());
  320. host.Actions.SelectMany(x => x.Filters.HandlerAware)
  321. .Union(host.Filters.HandlerAware)
  322. .Apply(x => x.MakeAwareOf(this));
  323. CreateActionMessage();
  324. action = host.GetAction(actionMessage);
  325. action.Completed += delegate { OnCompleted(); };
  326. }
  327. /// <summary>
  328. /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
  329. /// </summary>
  330. /// <returns>
  331. /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
  332. /// </returns>
  333. public override string ToString()
  334. {
  335. return "Command: " + Command;
  336. }
  337. /// <summary>
  338. /// Gets the default handlers for this type of message.
  339. /// </summary>
  340. /// <param name="node">The node to get default handlers for.</param>
  341. /// <returns></returns>
  342. public IEnumerable<IRoutedMessageHandler> GetDefaultHandlers(IInteractionNode node)
  343. {
  344. return actionMessage.GetDefaultHandlers(node);
  345. }
  346. }
  347. }
  348. #endif