From 92568e0cbf36e34b3c83b82255df975811d8a00a Mon Sep 17 00:00:00 2001 From: Antony Male Date: Mon, 3 Mar 2014 21:58:19 +0000 Subject: [PATCH] Document CommandAction and EventAction, and make them consistent --- Samples/Stylet.Samples.Hello/ShellView.xaml | 2 +- .../Stylet.Samples.Hello/ShellViewModel.cs | 11 ++++- Stylet/ActionExtension.cs | 4 +- Stylet/{ActionCommand.cs => CommandAction.cs} | 45 +++++++++++++++---- Stylet/{EventCommand.cs => EventAction.cs} | 35 +++++++++++---- 5 files changed, 77 insertions(+), 20 deletions(-) rename Stylet/{ActionCommand.cs => CommandAction.cs} (66%) rename Stylet/{EventCommand.cs => EventAction.cs} (50%) diff --git a/Samples/Stylet.Samples.Hello/ShellView.xaml b/Samples/Stylet.Samples.Hello/ShellView.xaml index 4878b17..e944ad2 100644 --- a/Samples/Stylet.Samples.Hello/ShellView.xaml +++ b/Samples/Stylet.Samples.Hello/ShellView.xaml @@ -4,7 +4,7 @@ xmlns:s="http://github.com/canton7/Stylet" Height="300" Width="300"> - + diff --git a/Samples/Stylet.Samples.Hello/ShellViewModel.cs b/Samples/Stylet.Samples.Hello/ShellViewModel.cs index e9d45b3..df3b723 100644 --- a/Samples/Stylet.Samples.Hello/ShellViewModel.cs +++ b/Samples/Stylet.Samples.Hello/ShellViewModel.cs @@ -9,13 +9,22 @@ namespace Stylet.Samples.Hello { class ShellViewModel : Screen { - public string Name { get; set; } + private string _name; + public string Name + { + get { return this._name; } + set { SetAndNotify(ref this._name, value); this.NotifyOfPropertyChange(() => this.CanSayHello); } + } public ShellViewModel() { this.DisplayName = "Hello, Stylet"; } + public bool CanSayHello + { + get { return !String.IsNullOrEmpty(this.Name); } + } public void SayHello() { MessageBox.Show(String.Format("Hello, {0}", this.Name)); // Don't do this diff --git a/Stylet/ActionExtension.cs b/Stylet/ActionExtension.cs index 05ca3bf..9c332db 100644 --- a/Stylet/ActionExtension.cs +++ b/Stylet/ActionExtension.cs @@ -30,13 +30,13 @@ namespace Stylet var propertyAsDependencyProperty = valueService.TargetProperty as DependencyProperty; if (propertyAsDependencyProperty != null && propertyAsDependencyProperty.PropertyType == typeof(ICommand)) { - return new ActionCommand((FrameworkElement)valueService.TargetObject, this.Method); + return new CommandAction((FrameworkElement)valueService.TargetObject, this.Method); } var propertyAsEventInfo = valueService.TargetProperty as EventInfo; if (propertyAsEventInfo != null) { - var ec = new EventCommand((FrameworkElement)valueService.TargetObject, propertyAsEventInfo, this.Method); + var ec = new EventAction((FrameworkElement)valueService.TargetObject, propertyAsEventInfo, this.Method); return ec.GetDelegate(); } diff --git a/Stylet/ActionCommand.cs b/Stylet/CommandAction.cs similarity index 66% rename from Stylet/ActionCommand.cs rename to Stylet/CommandAction.cs index 872f361..e52b8bb 100644 --- a/Stylet/ActionCommand.cs +++ b/Stylet/CommandAction.cs @@ -13,23 +13,52 @@ using Expressions = System.Linq.Expressions; namespace Stylet { - public class ActionCommand : ICommand + /// + /// ICommand returned by ActionExtension for binding buttons, etc, to methods on a ViewModel. + /// If the method has a parameter, CommandParameter is passed + /// + /// + /// Watches the current View.ActionTarget, and looks for a method with the given name, calling it when the ICommand is called. + /// If a bool property with name Get(methodName) exists, it will be observed and used to enable/disable the ICommand. + /// + public class CommandAction : ICommand { + /// + /// View to grab the View.ActionTarget from + /// private FrameworkElement subject; + + /// + /// Method name. E.g. if someone's gone Buttom Command="{s:Action MyMethod}", this is MyMethod. + /// private string methodName; + + /// + /// Generated accessor to grab the value of the guard property, or null if there is none + /// private Func guardPropertyGetter; + + /// + /// MethodInfo for the method to call. This has to exist, or we throw a wobbly + /// private MethodInfo targetMethodInfo; private object target; - public ActionCommand(FrameworkElement subject, string methodName) + /// + /// Create a new ActionCommand + /// + /// View to grab the View.ActionTarget from + /// Method name. the MyMethod in Buttom Command="{s:Action MyMethod}". + public CommandAction(FrameworkElement subject, string methodName) { this.subject = subject; this.methodName = methodName; - this.UpdateGuardHandler(); + this.UpdateGuardAndMethod(); - DependencyPropertyDescriptor.FromProperty(View.ActionTargetProperty, typeof(View)).AddValueChanged(this.subject, (o, e) => this.UpdateGuardHandler()); + // Observe the View.ActionTarget for changes, and re-bind the guard property and MethodInfo if it changes + DependencyPropertyDescriptor.FromProperty(View.ActionTargetProperty, typeof(View)).AddValueChanged(this.subject, (o, e) => this.UpdateGuardAndMethod()); } private string GuardName @@ -37,7 +66,7 @@ namespace Stylet get { return "Can" + this.methodName; } } - private void UpdateGuardHandler() + private void UpdateGuardAndMethod() { var newTarget = View.GetActionTarget(this.subject); MethodInfo targetMethodInfo = null; @@ -50,9 +79,9 @@ namespace Stylet var guardPropertyInfo = newTargetType.GetProperty(this.GuardName); if (guardPropertyInfo != null && guardPropertyInfo.PropertyType == typeof(bool)) { - var param = Expressions.Expression.Parameter(typeof(bool), "returnValue"); - var propertyAccess = Expressions.Expression.Property(param, guardPropertyInfo); - this.guardPropertyGetter = Expressions.Expression.Lambda>(propertyAccess, param).Compile(); + var targetExpression = Expressions.Expression.Constant(newTarget); + var propertyAccess = Expressions.Expression.Property(targetExpression, guardPropertyInfo); + this.guardPropertyGetter = Expressions.Expression.Lambda>(propertyAccess).Compile(); } targetMethodInfo = newTargetType.GetMethod(this.methodName); diff --git a/Stylet/EventCommand.cs b/Stylet/EventAction.cs similarity index 50% rename from Stylet/EventCommand.cs rename to Stylet/EventAction.cs index 9923035..6725505 100644 --- a/Stylet/EventCommand.cs +++ b/Stylet/EventAction.cs @@ -8,36 +8,55 @@ using System.Windows; namespace Stylet { - public class EventCommand + /// + /// Created by ActionExtension, this can return a delegate suitable adding binding to an event, and can call a method on the View.ActionTarget + /// + public class EventAction { + /// + /// View whose View.ActionTarget we watch + /// private FrameworkElement subject; + + /// + /// Property on the WPF element we're returning a delegate for + /// private EventInfo targetProperty; + + /// + /// The MyMethod in {s:Action MyMethod}, this is what we call when the event's fired + /// private string methodName; - public EventCommand(FrameworkElement subject, EventInfo targetProperty, string methodName) + /// + /// Create a new EventAction + /// + /// View whose View.ActionTarget we watch + /// Property on the WPF element we're returning a delegate for + /// The MyMethod in {s:Action MyMethod}, this is what we call when the event's fired + public EventAction(FrameworkElement subject, EventInfo targetProperty, string methodName) { this.subject = subject; this.targetProperty = targetProperty; this.methodName = methodName; } + /// + /// Return a delegate which can be added to the targetProperty + /// public Delegate GetDelegate() { - Delegate del = null; - var methodInfo = this.GetType().GetMethod("InvokeCommand", BindingFlags.NonPublic | BindingFlags.Instance); var parameterType = this.targetProperty.EventHandlerType; - del = Delegate.CreateDelegate(parameterType, this, methodInfo); - - return del; + return Delegate.CreateDelegate(parameterType, this, methodInfo); } private void InvokeCommand(object sender, RoutedEventArgs e) { var target = View.GetActionTarget(this.subject); if (target == null) - throw new Exception("Target not set"); + return; var methodInfo = target.GetType().GetMethod(this.methodName); if (methodInfo == null)