Как привязать команду в 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
сохраняется в 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();
}
}
Если вы посмотрите на этот образец здесь: 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 и действует в поведении.