mirror of https://github.com/AMT-Cheif/Stylet.git
Add support for CommandBinding in Actions
CommandBinding isn't a DependencyObject, so we can't get its DataContext or View.ActionTarget -- we can only use the IRootObjectProvider.RootObject. This should be good enough for most cases, as these tend to get installed at the root of a window. Fixes #50
This commit is contained in:
parent
61fcf4617c
commit
2027fad730
|
@ -25,6 +25,7 @@ obj/
|
|||
[Bb]in
|
||||
[Dd]ebug*/
|
||||
[Rr]elease*/
|
||||
*.vs
|
||||
|
||||
#Project files
|
||||
[Bb]uild/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>Stylet.ruleset</CodeAnalysisRuleSet>
|
||||
<DocumentationFile>bin\Debug\Stylet.xml</DocumentationFile>
|
||||
<LangVersion>5</LangVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
|
@ -35,6 +35,7 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<DocumentationFile>bin\Release\Stylet.xml</DocumentationFile>
|
||||
<CodeAnalysisRuleSet>Stylet.ruleset</CodeAnalysisRuleSet>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationCore" />
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<IProvideValueTarget> provideValueTarget;
|
||||
private Mock<IRootObjectProvider> rootObjectProvider;
|
||||
private Mock<IServiceProvider> 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<IProvideValueTarget>();
|
||||
this.provideValueTarget.Setup(x => x.TargetObject).Returns(new FrameworkElement());
|
||||
|
||||
this.rootObjectProvider = new Mock<IRootObjectProvider>();
|
||||
|
||||
this.serviceProvider = new Mock<IServiceProvider>();
|
||||
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<ArgumentException>(() => 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<ExecutedRoutedEventHandler>(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<InvalidOperationException>(() => this.actionExtension.ProvideValue(this.serviceProvider.Object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue