From 51007f4a226adbc87b431556876eca609520afc1 Mon Sep 17 00:00:00 2001 From: Antony Male Date: Tue, 24 Feb 2015 15:37:28 +0000 Subject: [PATCH] Add support for DependencyPropertyChangedEventHandler to EventAction --- Stylet/Xaml/EventAction.cs | 40 ++++++++++++++++++++++++----- StyletUnitTests/EventActionTests.cs | 36 ++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/Stylet/Xaml/EventAction.cs b/Stylet/Xaml/EventAction.cs index e3c59d6..63ff3bd 100644 --- a/Stylet/Xaml/EventAction.cs +++ b/Stylet/Xaml/EventAction.cs @@ -14,7 +14,11 @@ namespace Stylet.Xaml public class EventAction : ActionBase { private static readonly ILogger logger = LogManager.GetLogger(typeof(EventAction)); - private static readonly MethodInfo invokeCommandMethodInfo = typeof(EventAction).GetMethod("InvokeCommand", BindingFlags.NonPublic | BindingFlags.Instance); + private static readonly MethodInfo[] invokeCommandMethodInfos = new[] + { + typeof(EventAction).GetMethod("InvokeEventArgsCommand", BindingFlags.NonPublic | BindingFlags.Instance), + typeof(EventAction).GetMethod("InvokeDependencyCommand", BindingFlags.NonPublic | BindingFlags.Instance), + }; /// /// Type of event handler @@ -49,8 +53,8 @@ namespace Stylet.Xaml { var methodParameters = targetMethodInfo.GetParameters(); if (!(methodParameters.Length == 0 || - (methodParameters.Length == 1 && typeof(EventArgs).IsAssignableFrom(methodParameters[0].ParameterType)) || - (methodParameters.Length == 2 && typeof(EventArgs).IsAssignableFrom(methodParameters[1].ParameterType)))) + (methodParameters.Length == 1 && (typeof(EventArgs).IsAssignableFrom(methodParameters[0].ParameterType) || methodParameters[0].ParameterType == typeof(DependencyPropertyChangedEventArgs))) || + (methodParameters.Length == 2 && (typeof(EventArgs).IsAssignableFrom(methodParameters[1].ParameterType) || methodParameters[1].ParameterType == typeof(DependencyPropertyChangedEventArgs))))) { var e = new ActionSignatureInvalidException(String.Format("Method {0} on {1} must have the signatures void Method(), void Method(EventArgsOrSubClass e), or void Method(object sender, EventArgsOrSubClass e)", this.MethodName, newTargetType.Name)); logger.Error(e); @@ -64,18 +68,40 @@ namespace Stylet.Xaml /// An event hander, which, when invoked, will invoke the action public Delegate GetDelegate() { - var del = Delegate.CreateDelegate(this.eventHandlerType, this, invokeCommandMethodInfo, false); + Delegate del = null; + foreach (var invokeCommandMethodInfo in invokeCommandMethodInfos) + { + del = Delegate.CreateDelegate(this.eventHandlerType, this, invokeCommandMethodInfo, false); + if (del != null) + break; + } + if (del == null) { - var e = new ActionEventSignatureInvalidException(String.Format("Event being bound to does not have the '(object sender, EventArgsOrSubclass e)' signature we were expecting. Method {0} on target {1}", this.MethodName, this.Target)); + var msg = String.Format("Event being bound to does not have a signature we know about. Method {0} on target {1}. Valid signatures are:" + + "Valid signatures are:\n" + + " - '(object sender, EventArgsOrSubclass e)'\n" + + " - '(object sender, DependencyPropertyChangedEventArgs e)'", this.MethodName, this.Target); + var e = new ActionEventSignatureInvalidException(msg); logger.Error(e); throw e; } + return del; } + private void InvokeDependencyCommand(object sender, DependencyPropertyChangedEventArgs e) + { + this.InvokeCommand(sender, e); + } + + private void InvokeEventArgsCommand(object sender, EventArgs e) + { + this.InvokeCommand(sender, e); + } + // ReSharper disable once UnusedMember.Local - private void InvokeCommand(object sender, EventArgs e) + private void InvokeCommand(object sender, object e) { // If we've made it this far and the target is still the default, then something's wrong // Make sure they know @@ -83,7 +109,7 @@ namespace Stylet.Xaml { var ex = new ActionNotSetException(String.Format("View.ActionTarget not on control {0} (method {1}). " + "This probably means the control hasn't inherited it from a parent, e.g. because a ContextMenu or Popup sits in the visual tree. " + - "You will need so set 's:View.ActionTarget' explicitly. See the wiki for more details.", this.Subject, this.MethodName)); + "You will need so set 's:View.ActionTarget' explicitly. See the wiki section \"Actions\" for more details.", this.Subject, this.MethodName)); logger.Error(ex); throw ex; } diff --git a/StyletUnitTests/EventActionTests.cs b/StyletUnitTests/EventActionTests.cs index 0c0fc4e..a5c918a 100644 --- a/StyletUnitTests/EventActionTests.cs +++ b/StyletUnitTests/EventActionTests.cs @@ -21,6 +21,7 @@ namespace StyletUnitTests #pragma warning disable 0067 public event EventHandler SimpleEventHandler; public event Action BadEventHandler; + public event DependencyPropertyChangedEventHandler DependencyChangedEventHandler; #pragma warning restore 0067 } @@ -57,6 +58,18 @@ namespace StyletUnitTests this.EventArgs = e; } + public DependencyPropertyChangedEventArgs DependencyChangedEventArgs; + public void DoSomethingWithDependencyChangedEventArgs(DependencyPropertyChangedEventArgs e) + { + this.DependencyChangedEventArgs = e; + } + + public void DoSomethingWithObjectAndDependencyChangedEventArgs(object sender, DependencyPropertyChangedEventArgs e) + { + this.Sender = sender; + this.DependencyChangedEventArgs = e; + } + public void DoSomethingUnsuccessfully() { throw new InvalidOperationException("foo"); @@ -70,6 +83,7 @@ namespace StyletUnitTests private DependencyObject subject; private Target target; private EventInfo eventInfo; + private EventInfo dependencyChangedEventInfo; [SetUp] public void SetUp() @@ -77,6 +91,7 @@ namespace StyletUnitTests this.target = new Target(); this.subject = new Subject(); this.eventInfo = typeof(Subject).GetEvent("SimpleEventHandler"); + this.dependencyChangedEventInfo = typeof(Subject).GetEvent("DependencyChangedEventHandler"); View.SetActionTarget(this.subject, this.target); } @@ -175,6 +190,27 @@ namespace StyletUnitTests Assert.AreEqual(arg, this.target.EventArgs); } + [Test] + public void InvokingCommandCallsMethodWithDependencyChangedEventArgs() + { + var cmd = new EventAction(this.subject, this.dependencyChangedEventInfo.EventHandlerType, "DoSomethingWithDependencyChangedEventArgs", ActionUnavailableBehaviour.Enable, ActionUnavailableBehaviour.Enable); + var arg = new DependencyPropertyChangedEventArgs(); + cmd.GetDelegate().DynamicInvoke(null, arg); + Assert.AreEqual(arg, this.target.DependencyChangedEventArgs); + } + + [Test] + public void InvokingCommandCallsMethodWithSenderAndDependencyChangedEventArgs() + { + var cmd = new EventAction(this.subject, this.dependencyChangedEventInfo.EventHandlerType, "DoSomethingWithObjectAndDependencyChangedEventArgs", ActionUnavailableBehaviour.Enable, ActionUnavailableBehaviour.Enable); + var sender = new object(); + var arg = new DependencyPropertyChangedEventArgs(); + cmd.GetDelegate().DynamicInvoke(sender, arg); + + Assert.AreEqual(sender, this.target.Sender); + Assert.AreEqual(arg, this.target.DependencyChangedEventArgs); + } + [Test] public void BadEventHandlerSignatureThrows() {