Document CommandAction and EventAction, and make them consistent

This commit is contained in:
Antony Male 2014-03-03 21:58:19 +00:00
parent 7ee1f0ac0a
commit 92568e0cbf
5 changed files with 77 additions and 20 deletions

View File

@ -4,7 +4,7 @@
xmlns:s="http://github.com/canton7/Stylet" xmlns:s="http://github.com/canton7/Stylet"
Height="300" Width="300"> Height="300" Width="300">
<StackPanel> <StackPanel>
<TextBox Text="{Binding Name}"/> <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{s:Action SayHello}">Say Hello</Button> <Button Command="{s:Action SayHello}">Say Hello</Button>
</StackPanel> </StackPanel>
</Window> </Window>

View File

@ -9,13 +9,22 @@ namespace Stylet.Samples.Hello
{ {
class ShellViewModel : Screen 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() public ShellViewModel()
{ {
this.DisplayName = "Hello, Stylet"; this.DisplayName = "Hello, Stylet";
} }
public bool CanSayHello
{
get { return !String.IsNullOrEmpty(this.Name); }
}
public void SayHello() public void SayHello()
{ {
MessageBox.Show(String.Format("Hello, {0}", this.Name)); // Don't do this MessageBox.Show(String.Format("Hello, {0}", this.Name)); // Don't do this

View File

@ -30,13 +30,13 @@ namespace Stylet
var propertyAsDependencyProperty = valueService.TargetProperty as DependencyProperty; var propertyAsDependencyProperty = valueService.TargetProperty as DependencyProperty;
if (propertyAsDependencyProperty != null && propertyAsDependencyProperty.PropertyType == typeof(ICommand)) 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; var propertyAsEventInfo = valueService.TargetProperty as EventInfo;
if (propertyAsEventInfo != null) 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(); return ec.GetDelegate();
} }

View File

@ -13,23 +13,52 @@ using Expressions = System.Linq.Expressions;
namespace Stylet namespace Stylet
{ {
public class ActionCommand : ICommand /// <summary>
/// ICommand returned by ActionExtension for binding buttons, etc, to methods on a ViewModel.
/// If the method has a parameter, CommandParameter is passed
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public class CommandAction : ICommand
{ {
/// <summary>
/// View to grab the View.ActionTarget from
/// </summary>
private FrameworkElement subject; private FrameworkElement subject;
/// <summary>
/// Method name. E.g. if someone's gone Buttom Command="{s:Action MyMethod}", this is MyMethod.
/// </summary>
private string methodName; private string methodName;
/// <summary>
/// Generated accessor to grab the value of the guard property, or null if there is none
/// </summary>
private Func<bool> guardPropertyGetter; private Func<bool> guardPropertyGetter;
/// <summary>
/// MethodInfo for the method to call. This has to exist, or we throw a wobbly
/// </summary>
private MethodInfo targetMethodInfo; private MethodInfo targetMethodInfo;
private object target; private object target;
public ActionCommand(FrameworkElement subject, string methodName) /// <summary>
/// Create a new ActionCommand
/// </summary>
/// <param name="subject">View to grab the View.ActionTarget from</param>
/// <param name="methodName">Method name. the MyMethod in Buttom Command="{s:Action MyMethod}".</param>
public CommandAction(FrameworkElement subject, string methodName)
{ {
this.subject = subject; this.subject = subject;
this.methodName = methodName; 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 private string GuardName
@ -37,7 +66,7 @@ namespace Stylet
get { return "Can" + this.methodName; } get { return "Can" + this.methodName; }
} }
private void UpdateGuardHandler() private void UpdateGuardAndMethod()
{ {
var newTarget = View.GetActionTarget(this.subject); var newTarget = View.GetActionTarget(this.subject);
MethodInfo targetMethodInfo = null; MethodInfo targetMethodInfo = null;
@ -50,9 +79,9 @@ namespace Stylet
var guardPropertyInfo = newTargetType.GetProperty(this.GuardName); var guardPropertyInfo = newTargetType.GetProperty(this.GuardName);
if (guardPropertyInfo != null && guardPropertyInfo.PropertyType == typeof(bool)) if (guardPropertyInfo != null && guardPropertyInfo.PropertyType == typeof(bool))
{ {
var param = Expressions.Expression.Parameter(typeof(bool), "returnValue"); var targetExpression = Expressions.Expression.Constant(newTarget);
var propertyAccess = Expressions.Expression.Property(param, guardPropertyInfo); var propertyAccess = Expressions.Expression.Property(targetExpression, guardPropertyInfo);
this.guardPropertyGetter = Expressions.Expression.Lambda<Func<bool>>(propertyAccess, param).Compile(); this.guardPropertyGetter = Expressions.Expression.Lambda<Func<bool>>(propertyAccess).Compile();
} }
targetMethodInfo = newTargetType.GetMethod(this.methodName); targetMethodInfo = newTargetType.GetMethod(this.methodName);

View File

@ -8,36 +8,55 @@ using System.Windows;
namespace Stylet namespace Stylet
{ {
public class EventCommand /// <summary>
/// Created by ActionExtension, this can return a delegate suitable adding binding to an event, and can call a method on the View.ActionTarget
/// </summary>
public class EventAction
{ {
/// <summary>
/// View whose View.ActionTarget we watch
/// </summary>
private FrameworkElement subject; private FrameworkElement subject;
/// <summary>
/// Property on the WPF element we're returning a delegate for
/// </summary>
private EventInfo targetProperty; private EventInfo targetProperty;
/// <summary>
/// The MyMethod in {s:Action MyMethod}, this is what we call when the event's fired
/// </summary>
private string methodName; private string methodName;
public EventCommand(FrameworkElement subject, EventInfo targetProperty, string methodName) /// <summary>
/// Create a new EventAction
/// </summary>
/// <param name="subject">View whose View.ActionTarget we watch</param>
/// <param name="targetProperty">Property on the WPF element we're returning a delegate for</param>
/// <param name="methodName">The MyMethod in {s:Action MyMethod}, this is what we call when the event's fired</param>
public EventAction(FrameworkElement subject, EventInfo targetProperty, string methodName)
{ {
this.subject = subject; this.subject = subject;
this.targetProperty = targetProperty; this.targetProperty = targetProperty;
this.methodName = methodName; this.methodName = methodName;
} }
/// <summary>
/// Return a delegate which can be added to the targetProperty
/// </summary>
public Delegate GetDelegate() public Delegate GetDelegate()
{ {
Delegate del = null;
var methodInfo = this.GetType().GetMethod("InvokeCommand", BindingFlags.NonPublic | BindingFlags.Instance); var methodInfo = this.GetType().GetMethod("InvokeCommand", BindingFlags.NonPublic | BindingFlags.Instance);
var parameterType = this.targetProperty.EventHandlerType; var parameterType = this.targetProperty.EventHandlerType;
del = Delegate.CreateDelegate(parameterType, this, methodInfo); return Delegate.CreateDelegate(parameterType, this, methodInfo);
return del;
} }
private void InvokeCommand(object sender, RoutedEventArgs e) private void InvokeCommand(object sender, RoutedEventArgs e)
{ {
var target = View.GetActionTarget(this.subject); var target = View.GetActionTarget(this.subject);
if (target == null) if (target == null)
throw new Exception("Target not set"); return;
var methodInfo = target.GetType().GetMethod(this.methodName); var methodInfo = target.GetType().GetMethod(this.methodName);
if (methodInfo == null) if (methodInfo == null)