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"
Height="300" Width="300">
<StackPanel>
<TextBox Text="{Binding Name}"/>
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<Button Command="{s:Action SayHello}">Say Hello</Button>
</StackPanel>
</Window>

View File

@ -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

View File

@ -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();
}

View File

@ -13,23 +13,52 @@ using Expressions = System.Linq.Expressions;
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;
/// <summary>
/// Method name. E.g. if someone's gone Buttom Command="{s:Action MyMethod}", this is MyMethod.
/// </summary>
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;
/// <summary>
/// MethodInfo for the method to call. This has to exist, or we throw a wobbly
/// </summary>
private MethodInfo targetMethodInfo;
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.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<Func<bool>>(propertyAccess, param).Compile();
var targetExpression = Expressions.Expression.Constant(newTarget);
var propertyAccess = Expressions.Expression.Property(targetExpression, guardPropertyInfo);
this.guardPropertyGetter = Expressions.Expression.Lambda<Func<bool>>(propertyAccess).Compile();
}
targetMethodInfo = newTargetType.GetMethod(this.methodName);

View File

@ -8,36 +8,55 @@ using System.Windows;
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;
/// <summary>
/// Property on the WPF element we're returning a delegate for
/// </summary>
private EventInfo targetProperty;
/// <summary>
/// The MyMethod in {s:Action MyMethod}, this is what we call when the event's fired
/// </summary>
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.targetProperty = targetProperty;
this.methodName = methodName;
}
/// <summary>
/// Return a delegate which can be added to the targetProperty
/// </summary>
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)