diff --git a/.gitignore b/.gitignore
index 003b806..0573f54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,6 +25,7 @@ obj/
[Bb]in
[Dd]ebug*/
[Rr]elease*/
+*.vs
#Project files
[Bb]uild/
diff --git a/Stylet.sln b/Stylet.sln
index c7839e9..ad5ad5d 100644
--- a/Stylet.sln
+++ b/Stylet.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.30110.0
+# Visual Studio 15
+VisualStudioVersion = 15.0.27703.2026
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Stylet", "Stylet\Stylet.csproj", "{2435BD00-AC12-48B0-AD36-9BAB2FDEC3F5}"
EndProject
@@ -31,4 +31,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {6A75A07E-E87F-4A90-BA14-D1237C7A3C67}
+ EndGlobalSection
EndGlobal
diff --git a/Stylet/Stylet.csproj b/Stylet/Stylet.csproj
index 652f3e8..ef516c0 100644
--- a/Stylet/Stylet.csproj
+++ b/Stylet/Stylet.csproj
@@ -24,7 +24,7 @@
4
Stylet.ruleset
bin\Debug\Stylet.xml
- 5
+ latest
pdbonly
@@ -35,6 +35,7 @@
4
bin\Release\Stylet.xml
Stylet.ruleset
+ latest
diff --git a/Stylet/Xaml/ActionExtension.cs b/Stylet/Xaml/ActionExtension.cs
index 9e75d67..16ac98e 100644
--- a/Stylet/Xaml/ActionExtension.cs
+++ b/Stylet/Xaml/ActionExtension.cs
@@ -101,44 +101,56 @@ namespace Stylet.Xaml
throw new InvalidOperationException("Method has not been set");
var valueService = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
-
- // Seems this is the case when we're in a template. We'll get called again properly in a second.
- // http://social.msdn.microsoft.com/Forums/vstudio/en-US/a9ead3d5-a4e4-4f9c-b507-b7a7d530c6a9/gaining-access-to-target-object-instead-of-shareddp-in-custom-markupextensions-providevalue-method?forum=wpf
- if (!(valueService.TargetObject is DependencyObject))
- return this;
-
- var targetObject = (DependencyObject)valueService.TargetObject;
-
var rootObjectProvider = (IRootObjectProvider)serviceProvider.GetService(typeof(IRootObjectProvider));
- var rootObject = rootObjectProvider == null ? null : rootObjectProvider.RootObject as DependencyObject;
+ var rootObject = rootObjectProvider?.RootObject as DependencyObject;
- var propertyAsDependencyProperty = valueService.TargetProperty as DependencyProperty;
- if (propertyAsDependencyProperty != null && propertyAsDependencyProperty.PropertyType == typeof(ICommand))
+ switch (valueService.TargetObject)
{
- // If they're in design mode and haven't set View.ActionTarget, default to looking sensible
- return new CommandAction(targetObject, rootObject, this.Method, this.CommandNullTargetBehaviour, this.CommandActionNotFoundBehaviour);
+ case DependencyObject targetObject:
+ return this.HandleDependencyObject(valueService, targetObject, rootObject);
+ case CommandBinding commandBinding:
+ return this.HandleCommandBinding(rootObject, ((EventInfo)valueService.TargetProperty).EventHandlerType);
+ default:
+ // Seems this is the case when we're in a template. We'll get called again properly in a second.
+ // http://social.msdn.microsoft.com/Forums/vstudio/en-US/a9ead3d5-a4e4-4f9c-b507-b7a7d530c6a9/gaining-access-to-target-object-instead-of-shareddp-in-custom-markupextensions-providevalue-method?forum=wpf
+ return this;
}
+ }
- var propertyAsEventInfo = valueService.TargetProperty as EventInfo;
- if (propertyAsEventInfo != null)
+ private object HandleDependencyObject(IProvideValueTarget valueService, DependencyObject targetObject, DependencyObject rootObject)
+ {
+ switch (valueService.TargetProperty)
{
- var ec = new EventAction(targetObject, rootObject, propertyAsEventInfo.EventHandlerType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
- return ec.GetDelegate();
- }
-
- // For attached events
- var propertyAsMethodInfo = valueService.TargetProperty as MethodInfo;
- if (propertyAsMethodInfo != null)
- {
- var parameters = propertyAsMethodInfo.GetParameters();
- if (parameters.Length == 2 && typeof(Delegate).IsAssignableFrom(parameters[1].ParameterType))
+ case DependencyProperty dependencyProperty when dependencyProperty.PropertyType == typeof(ICommand):
+ // If they're in design mode and haven't set View.ActionTarget, default to looking sensible
+ return new CommandAction(targetObject, rootObject, this.Method, this.CommandNullTargetBehaviour, this.CommandActionNotFoundBehaviour);
+ case EventInfo eventInfo:
{
- var ec = new EventAction(targetObject, rootObject, parameters[1].ParameterType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
+ var ec = new EventAction(targetObject, rootObject, eventInfo.EventHandlerType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
return ec.GetDelegate();
}
+ case MethodInfo methodInfo: // For attached events
+ {
+ var parameters = methodInfo.GetParameters();
+ if (parameters.Length == 2 && typeof(Delegate).IsAssignableFrom(parameters[1].ParameterType))
+ {
+ var ec = new EventAction(targetObject, rootObject, parameters[1].ParameterType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
+ return ec.GetDelegate();
+ }
+ throw new ArgumentException("Action used with an attached event (or something similar) which didn't follow the normal pattern");
+ }
+ default:
+ throw new ArgumentException("Can only use ActionExtension with a Command property or an event handler");
}
-
- throw new ArgumentException("Can only use ActionExtension with a Command property or an event handler");
+ }
+
+ private object HandleCommandBinding(DependencyObject rootObject, Type propertyType)
+ {
+ if (rootObject == null)
+ throw new InvalidOperationException("Action may only be used with CommandBinding from a XAML view (unable to retrieve IRootObjectProvider.RootObject)");
+
+ var ec = new EventAction(rootObject, null, propertyType, this.Method, this.EventNullTargetBehaviour, this.EventActionNotFoundBehaviour);
+ return ec.GetDelegate();
}
}
diff --git a/StyletUnitTests/ActionExtensionTests.cs b/StyletUnitTests/ActionExtensionTests.cs
index e247ffc..1794a4b 100644
--- a/StyletUnitTests/ActionExtensionTests.cs
+++ b/StyletUnitTests/ActionExtensionTests.cs
@@ -4,7 +4,9 @@ using Stylet.Xaml;
using System;
using System.Windows;
using System.Windows.Controls;
+using System.Windows.Input;
using System.Windows.Markup;
+using System.Xaml;
namespace StyletUnitTests
{
@@ -13,6 +15,7 @@ namespace StyletUnitTests
{
private ActionExtension actionExtension;
private Mock provideValueTarget;
+ private Mock rootObjectProvider;
private Mock serviceProvider;
private class TestExtensions
@@ -20,14 +23,12 @@ namespace StyletUnitTests
public static readonly RoutedEvent TestEvent = EventManager.RegisterRoutedEvent("Test", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TestExtensions));
public static void AddTestHandler(DependencyObject d, RoutedEventHandler handler)
{
- UIElement uie = d as UIElement;
- if (uie != null)
+ if (d is UIElement uie)
uie.AddHandler(TestExtensions.TestEvent, handler);
}
public static void RemoveTestHandler(DependencyObject d, RoutedEventHandler handler)
{
- UIElement uie = d as UIElement;
- if (uie != null)
+ if (d is UIElement uie)
uie.RemoveHandler(TestExtensions.TestEvent, handler);
}
@@ -44,8 +45,11 @@ namespace StyletUnitTests
this.provideValueTarget = new Mock();
this.provideValueTarget.Setup(x => x.TargetObject).Returns(new FrameworkElement());
+ this.rootObjectProvider = new Mock();
+
this.serviceProvider = new Mock();
- serviceProvider.Setup(x => x.GetService(typeof(IProvideValueTarget))).Returns(provideValueTarget.Object);
+ this.serviceProvider.Setup(x => x.GetService(typeof(IProvideValueTarget))).Returns(this.provideValueTarget.Object);
+ this.serviceProvider.Setup(x => x.GetService(typeof(IRootObjectProvider))).Returns(this.rootObjectProvider.Object);
}
[Test]
@@ -100,5 +104,24 @@ namespace StyletUnitTests
Assert.Throws(() => this.actionExtension.ProvideValue(this.serviceProvider.Object));
}
+
+ [Test]
+ public void ReturnsEventActionIfTargetIsCommandBinding()
+ {
+ this.provideValueTarget.Setup(x => x.TargetObject).Returns(new CommandBinding());
+ this.provideValueTarget.Setup(x => x.TargetProperty).Returns(typeof(CommandBinding).GetEvent("Executed"));
+ this.rootObjectProvider.Setup(x => x.RootObject).Returns(new DependencyObject());
+
+ Assert.IsInstanceOf(this.actionExtension.ProvideValue(this.serviceProvider.Object));
+ }
+
+ [Test]
+ public void ThrowsIfTargetIsCommandBindingAndRootObjectNotSet()
+ {
+ this.provideValueTarget.Setup(x => x.TargetObject).Returns(new CommandBinding());
+ this.provideValueTarget.Setup(x => x.TargetProperty).Returns(typeof(CommandBinding).GetEvent("Executed"));
+
+ Assert.Throws(() => this.actionExtension.ProvideValue(this.serviceProvider.Object));
+ }
}
}