Как привязать команду в ViewModel к команде в поведении? - PullRequest
0 голосов
/ 20 ноября 2018

Проект WPF + Prism 7 + (шаблон Pure 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 всегда равна нулю (этоне привязан к команде в поведении), хотя я убедился, что она инициализируется в классе поведения.

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

Ответы [ 2 ]

0 голосов
/ 21 ноября 2018

Если вы посмотрите на этот пример здесь: 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 изначально имеет значение null.В конструкторе устанавливается новое RelayCommand, что означает, что в этом случае оно не равно NULL.

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


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

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

0 голосов
/ 21 ноября 2018

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

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

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

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

В-третьих, при использовании 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();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...