Это - приложение MVVM. Существует окно и связанный класс модели представления.
Существует TextBox
, Button
и ListBox
на форме. Кнопка связывается с DelegateCommand
это имеет CanExecute
функция. Идея состоит в том, что пользователь вводит некоторые данные в текстовое поле, кнопка нажатий и данные добавляются к полю списка.
Я хотел бы включить команду (и кнопка), когда пользователь вводит корректные данные в TextBox
. Вещи работают как это теперь:
CanExecute()
метод содержит код, который проверяет, корректны ли данные в свойстве, связанном с текстовым полем.UpdateSourceTrigger
установлен на PropertyChanged
и свойство в поле зрения модель обновляется после каждого ключевого пользователя нажатия. Проблема - это CanExecute()
не стреляет, когда пользователь вводит данные в текстовое поле. Это не стреляет, даже когда текстовое поле теряет фокус.
Как я мог сделать эту работу?
Править:
Комментарий Yanko ре:
Команда делегата реализована в шаблоне инструментария MVVM и когда Вы создаете новый проект MVVM, в решении существует команда Delegate. Так, как я видел в видео Призмы, это должно быть тем же классом (или по крайней мере очень похожий).
Вот отрывок XAML:
...
...
...
Модель View:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
OnPropertyChanged("ObjectName");
}
}
// Command bound to button
public ICommand AddObjectCommand
{
get
{
if (addObjectCommand == null)
{
addObjectCommand = new DelegateCommand(AddObject, CanAddObject);
}
return addObjectCommand;
}
}
private void AddObject()
{
if (ObjectName == null || ObjectName.Length == 0)
return;
objectNames.AddSourceFile(ObjectName);
OnPropertyChanged("ObjectNames"); // refresh listbox
}
private bool CanAddObject()
{
return ObjectName != null && ObjectName.Length > 0;
}
Поскольку я записал в первой части вопроса, после вещей работа:
ObjectName
инициирован на каждом нажатии клавиши в текстовом полеreturn true;
в CanAddObject()
, команда активна (кнопка к) Это смотрит на меня, что привязка корректна.
Вещь, которую я не знаю, состоит в том, как сделать CanExecute()
огонь в методе set ObjectName
свойство из вышеупомянутого кода.
Ре ответы Ben и Abe:
CanExecuteChanged()
обработчик событий, и компилятор жалуется:
Событие 'System. Windows. Input. ICommand. CanExecuteChanged' может только появиться на левой стороне + = или - =
существует еще только два члена ICommand
: Execute()
и CanExecute()
У Вас есть некоторый пример, который показывает, как я могу выполнить вызов команды CanExecute()
.
Я нашел класс помощника менеджера по команде в DelegateCommand.cs
и я изучу его, возможно, существует некоторый механизм, который мог помочь.
Так или иначе идея, что для активации команды на основе ввода данных пользователем, нужно "пошагово переместить" объект команды в коде метода set свойства, выглядит неуклюжей. Это представит зависимости, и одна из больших точек MVVM уменьшает их.
Редактирование 2:
Я пытался активироваться CanExecute
путем вызова addObjectCommand.RaiseCanExecuteChanged()
кому: ObjectName
метод set свойства из вышеупомянутого кода. Это не помогает также. CanExecute()
запущен несколько раз, когда форма инициализируется, но после этого она никогда не выполняется снова. Это - код:
// Property bound to textbox
public string ObjectName
{
get { return objectName; }
set {
objectName = value;
addObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Редактирование 3: решение
Как Yanko Yankov и JerKimball записали, проблемой является статический ресурс. Когда я изменил кнопку, связывающую как предложенный Yanko:
вещи начали работать сразу. Мне даже не нужно RaiseCanExecuteChanged()
. Теперь CanExecute
огни автоматически.
Почему я использовал статический ресурс в первом месте?
Исходный код был из руководства инструментария WPF MVVM. Пример в том руководстве определяет команды как статический ресурс и затем связывает его с пунктом меню. Различие то, что вместо свойства строки в моем примере, физических трудах MVVM с ObservableCollection
.
Редактирование 4: Заключительное объяснение
Я наконец получил его. Все, что я должен был сделать, должно было прочитать комментарий в CommandReference
класс. Это говорит:
///
/// This class facilitates associating a key binding in XAML markup to a command
/// defined in a View Model by exposing a Command dependency property.
/// The class derives from Freezable to work around a limitation in WPF when
/// databinding from XAML.
///
Так, CommandReference
используется для KeyBinding
, это не для привязки в визуальных элементах. В вышеупомянутом коде ссылки команды, определенные в ресурсах, работали бы на KeyBinding, который я не имею на этом пользовательском элементе управления.
Конечно, пример кода, который шел с инструментарием WPF MVVM, был корректен, но я неправильно читал его и использовал CommandReference
в визуальной привязке элементов.
Этот WPF MVVM действительно иногда хитер.
Теперь с внесенными правками все выглядит намного яснее, спасибо! Это может быть глупый вопрос (я несколько устал от долгого рабочего дня), но почему бы вам не выполнить привязку к команде напрямую, а не через статический ресурс?
<Button Command="{Binding AddObjectCommand}">Add</Button>
Поскольку вы используете DelegateCommand, вы можете вызвать его метод RaiseCanExecuteChanged при изменении вашего свойства текста. Я не уверен, что вы пытаетесь достичь с помощью своего ресурса CommandReference, но обычно вы просто привязываете команды непосредственно к свойству Command элемента кнопки:
<TextBox Text="{Binding ObjectName, UpdateSourceTrigger=ValueChanged}" />
<Button Command="{Binding AddObjectCommand}" Content="Add" />
Это будет соответствующая часть вашей модели представления:
public string ObjectName
{
get { return objectName; }
set
{
if (value == objectName) return;
value = objectName;
AddObjectCommand.RaiseCanExecuteChanged();
OnPropertyChanged("ObjectName");
}
}
Попробуйте поднять CanExecuteChanged
при изменении вашего свойства. Привязка команд действительно отличается от привязки свойств, и кнопки, привязанные к командам, предупреждаются об изменении статуса с помощью события CanExecuteChanged
.
В вашем случае вы можете запустить проверку, выполнив PropertyChanged
связанного свойства, которое оценит его и установит внутренний флаг команды CanExecute
, а затем поднимет CanExecuteChanged
. Больше похоже на «толчок» в объект ICommand
, чем на «вытягивание».
Здесь повторяется Эйб, но "правильный" путь здесь - использовать:
public void RaiseCanExecuteChanged();
, указанный в DelegateCommand. Что касается зависимостей, я не думаю, что вы действительно делаете что-то «плохое», поднимая это значение, когда свойство, которое команда зависит от изменений в ViewModel. В этом случае связь более или менее полностью содержится в ViewModel.
Итак, в приведенном выше примере в установщике для «ObjectName» вы должны вызвать RaiseCanExecuteChanged в команде «AddObjectCommand».