trainings DependencyObject - custom command

烂漫一生 提交于 2019-12-25 09:20:03

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!