тренинги DependencyObject - пользовательская команда

Я пытаюсь создать команду, которая наследуется от DependencyObject и ICommand. У меня есть следующий код:

    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
    }
}

а также

    <Button Content="Test">
    <Button.Command>
        <command:CustomCommand  Command="{Binding Copy}" AfterCommand="{Binding AfterCopy}" />
    </Button.Command>
</Button>

Когда я нажимаю кнопку «Тест», Command и AfterCommand имеют значение null. У вас есть идея? Каков наилучший способ, потому что я не могу добавить ссылку ICommand в свою ViewModel.

Спасибо


person Cribanus    schedule 09.05.2017    source источник
comment
Измените на это: Command="{Binding Copy, PresentationTraceSources.TraceLevel=High}" и посмотрите, что вы видите в выводе отладки. Я подозреваю, что вы обнаружите, что контекст отсутствует, потому что его нет в визуальном дереве. Вы можете попробовать создать свою команду в качестве ресурса и/или попробовать различные стратегии связывания прокси.   -  person 15ee8f99-57ff-4f92-890c-b56153    schedule 09.05.2017
comment
Кстати, у вас есть ошибка в свойстве AfterCommand: оно передает CommandProperty в GetValue/SetValue, а не AfterCommandProperty.   -  person 15ee8f99-57ff-4f92-890c-b56153    schedule 09.05.2017


Ответы (1)


Ваш экземпляр CustomCommand отсутствует в визуальном дереве, поэтому привязка является проблемой. У него нет способа получить DataContext. Попробуйте поставить трассировку на привязку:

<Button.Command>
    <local:CustomCommand
        Command="{Binding TestAction, PresentationTraceSources.TraceLevel=High}"
        />
</Button.Command>

«Наставник Framework не найден» — это ошибка, которую вы увидите в выходных данных отладки. «Отсюда тебе туда не добраться» — так говорили на Дальнем Востоке. Контекст в XAML — это вопрос родителя к родителю, но у этой вещи, в том смысле, что здесь это имеет значение, нет родителя.

Но это легко исправить. Используйте связывающий прокси. Вот реализация городского велосипеда, которую я несколько раз украл из различных вопросов и ответов о переполнении стека:

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));
}

Определите экземпляр как ресурс в некоторой содержащей области, которая имеет DataContext, где находится желаемое свойство Action. {Binding} без пути просто возвращает DataContext, которая будет моделью просмотра окна в случае ниже.

<Window.Resources>
    <local:BindingProxy
        x:Key="MainViewModelBindingProxy"
        Data="{Binding}"
        />
</Window.Resources>

И использовать его так. Свойство Data элемента BindingProxy привязано к модели представления, поэтому используйте путь Data.WhateverPropertyYouWant. Я назвал свою собственность Action TestAction.

<Button
    Content="Custom Command Test"
    >
    <Button.Command>
        <local:CustomCommand
            Command="{Binding Data.TestAction, Source={StaticResource MainViewModelBindingProxy}}"
            />
    </Button.Command>
</Button>

N.B.

У вас также есть ошибка в вашем свойстве AfterCommand: оно передает CommandProperty в GetValue/SetValue, а не AfterCommandProperty.

person 15ee8f99-57ff-4f92-890c-b56153    schedule 09.05.2017