问题
I try to create Command which inherit from DependencyObject and ICommand. I have the following code:
public class CustomCommand : DependencyObject, ICommand
{
public static readonly DependencyProperty CommandProperty;
public static readonly DependencyProperty AfterCommandProperty;
static CustomCommand()
{
var ownerType = typeof(CustomCommand);
CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(Action), ownerType, new PropertyMetadata(null));
AfterCommandProperty = DependencyProperty.RegisterAttached("AfterCommand", typeof(Action), ownerType, new PropertyMetadata(null));
}
public Action Command
{
get => (Action)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public Action AfterCommand
{
get => (Action)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
// Command & AfterCommand are always null
}
}
and
<Button Content="Test">
<Button.Command>
<command:CustomCommand Command="{Binding Copy}" AfterCommand="{Binding AfterCopy}" />
</Button.Command>
</Button>
When I press Test button Command and AfterCommand are null. Do you have an idea ? What is the best way cause I can't add ICommand reference to my ViewModel.
Thanks
回答1:
Your CustomCommand instance isn't in the visual tree, so binding is a problem. It's got no way to get a DataContext. Try putting a trace on the binding:
<Button.Command>
<local:CustomCommand
Command="{Binding TestAction, PresentationTraceSources.TraceLevel=High}"
/>
</Button.Command>
"Framework mentor not found" is the error you'll see in the debug output. "Ya can't get there from here" is how they'd say that Down East. Context in XAML is a matter of parent-to-parent, but this thing has, in the sense that matters here, no parent.
But it's an easy fix. Use a binding proxy. Here's a town bike implementation that I've stolen several times from various questions and answers on Stack Overflow:
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
Define an instance as a resource in some containing scope that has the DataContext
where the desired Action
property lives. {Binding}
with no path just returns DataContext
, which will be the window's viewmodel in the case below.
<Window.Resources>
<local:BindingProxy
x:Key="MainViewModelBindingProxy"
Data="{Binding}"
/>
</Window.Resources>
And use it like so. The Data
property of BindingProxy
is bound to the viewmodel, so use a path of Data.WhateverPropertyYouWant
. I called my Action
property TestAction
.
<Button
Content="Custom Command Test"
>
<Button.Command>
<local:CustomCommand
Command="{Binding Data.TestAction, Source={StaticResource MainViewModelBindingProxy}}"
/>
</Button.Command>
</Button>
N.B.
You've also got a bug in your AfterCommand
property: It's passing CommandProperty
to GetValue/SetValue, not AfterCommandProperty
.
来源:https://stackoverflow.com/questions/43876685/trainings-dependencyobject-custom-command