Как привязать команду в ViewModel к команде в поведении?

Проект WPF + Prism 7 + (Чистый шаблон MVVM)

Просто у меня есть TextBox которые необходимо очистить при нажатии какой-либо кнопки (без нарушения паттерна MVVM)

<Button Command="{Binding ClearCommand}"/>
<TextBox Text="{Binding File}">
    <i:Interaction.Behaviors>
        <local:ClearTextBehavior ClearTextCommand="{Binding ClearCommand, Mode=OneWayToSource}" />
    </i:Interaction.Behaviors>
</TextBox>

ViewModel

public class ViewModel {
    public ICommand ClearCommand { get; set; }
}

Поведение

public class ClearTextBehavior : Behavior<TextBox>
{
    public ICommand ClearTextCommand
    {
        get { return (ICommand)GetValue(ClearTextCommandProperty); }
        set
        {
            SetValue(ClearTextCommandProperty, value);
            RaisePropertyChanged(); 
        }
    }

    public static readonly DependencyProperty ClearTextCommandProperty =
        DependencyProperty.Register(nameof(ClearTextCommand), typeof(ICommand), typeof(ClearTextBehavior));

    public ClearTextBehavior()
    {
        ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
    }

    private void ClearTextCommandExecuted()
    {
        this.AssociatedObject.Clear();
    }
}

Проблема в том, что команда в ViewModel всегда имеет значение null (она не привязана к команде в поведении), хотя я убедился, что она инициализируется в классе поведения.

ПРИМЕЧАНИЕ: пожалуйста, НЕ предлагайте устанавливать для свойства File пустую строку, потому что это всего лишь пример. В моем реальном случае мне нужно выделить весь текст, поэтому мне действительно нужен доступ к AssociatedObject поведения

2 ответа

Если я правильно понял ваш вопрос, вы хотите знать, почему ICommand в ViewModel не установлен на DelegateCommand определены в Behaviour,

Проблема в том, что ICommand и DelegateCommand не имеют прямой связи. Я полагаю, вы можете неправильно понять, как Binding работает и что происходит с их использованием.

Прежде всего, ICommand это "исходит" от Class и, следовательно, является ссылочным типом.

Во-вторых, ссылка на ICommand сохраняется в DependencyPropertyClearTextCommandProperty,

В-третьих, используя Binding в XAML что-то вроде этого происходит в виде кода C#:

Binding binding = new Binding();
binding.Path = new PropertyPath("ClearTextCommand");
binding.Source = ClearCommand; 
BindingOperations.SetBinding(TextBox.ClearTextCommandProperty, binding);

Теперь важная вещь: я не знаю точно, какое назначение произойдет первым, но обе строки переопределят Value ссылка в ClearTextCommandProperty!

//either here
SetBinding(TextBox.ClearTextCommandProperty, binding);

//or here 
ClearTextCommand = new DelegateCommand(ClearTextCommandExecuted);
//Which could be written as
SetValue(ClearTextCommandProperty, new DelegateCommand(ClearTextCommandExecuted));

Ни в коем случае нет такого назначения:

ViewModel.ClearCommand = SomeICommand;

Поэтому это Nullкак упомянул @Andy


Отредактировано, чтобы соответствовать выбрать весь текст

Кроме того, я предлагаю вам отказаться от этого сложного материала и использовать весь потенциал Interactivity Пакет как это:

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

<Button>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Click">
            <utils:SelectAllText TargetName="TextToSelect"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</Button>

<TextBox x:Name="TextToSelect" Text="{Binding File}"/>

И SelectAllText

public class SelectAllText : TargetedTriggerAction<TextBox>
{
    protected override void Invoke(object parameter)
    {
        if (Target == null) return;

        Target.SelectAll();
        Target.Focus();
    }
}

Если вы посмотрите на этот образец здесь: https://social.technet.microsoft.com/wiki/contents/articles/31915.wpf-mvvm-step-by-step-1.aspx Вы заметите, что у меня есть ICommand там и настроен для запуска метода.

Если бы это была просто ICommand с Get и Set, как у вас там, то это было бы NULL. Есть свойство, но оно равно нулю, пока не установлено что-либо.

Это очень неуклюжий способ реализации ICommand, но он не использует никаких внешних библиотек или чего-либо еще.

Если вы посмотрите вторую статью из этой серии, то в ней используются mvvmlight и relaycommand, поэтому создание команды не так сложно.

https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx

public RelayCommand AddListCommand { get; set; }

public MainWindowViewModel()
{
    AddListCommand = new RelayCommand(AddList);
}
private void AddList()
{
    stringList.Add(myString));
}

Если вы посмотрите на этот код, то AddListCommand изначально равен нулю. Он установлен в конструкторе на новый RelayCommand, что означает, что он не равен нулю.

Это довольно просто, но код команды находится в другом месте, чем свойство, поэтому обычно более элегантный подход. Как показано здесь: https://msdn.microsoft.com/en-gb/magazine/dn237302.aspx


Сказав все это. Выделение всего текста - это то, что нужно делать в представлении, а не в модели представления. Вы не должны действительно передавать часть пользовательского интерфейса из представления в модель представления.

Вместо команды вполне может быть, что вы должны связывать bool, который установлен в viewmodel и действует в поведении.

Другие вопросы по тегам