Включите кнопку на основе значения TextBox (WPF)

Это - приложение 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;
    }

Поскольку я записал в первой части вопроса, после вещей работа:

  • метод set свойства для 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 действительно иногда хитер.

9
задан 14 revs, 2 users 63% 23 May 2017 в 12:01
поделиться

4 ответа

Теперь с внесенными правками все выглядит намного яснее, спасибо! Это может быть глупый вопрос (я несколько устал от долгого рабочего дня), но почему бы вам не выполнить привязку к команде напрямую, а не через статический ресурс?

<Button Command="{Binding AddObjectCommand}">Add</Button>
4
ответ дан 4 December 2019 в 21:09
поделиться

Поскольку вы используете 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");
    }
}
3
ответ дан 4 December 2019 в 21:09
поделиться

Попробуйте поднять CanExecuteChanged при изменении вашего свойства. Привязка команд действительно отличается от привязки свойств, и кнопки, привязанные к командам, предупреждаются об изменении статуса с помощью события CanExecuteChanged .

В вашем случае вы можете запустить проверку, выполнив PropertyChanged связанного свойства, которое оценит его и установит внутренний флаг команды CanExecute , а затем поднимет CanExecuteChanged . Больше похоже на «толчок» в объект ICommand , чем на «вытягивание».

1
ответ дан 4 December 2019 в 21:09
поделиться

Здесь повторяется Эйб, но "правильный" путь здесь - использовать:

public void RaiseCanExecuteChanged();

, указанный в DelegateCommand. Что касается зависимостей, я не думаю, что вы действительно делаете что-то «плохое», поднимая это значение, когда свойство, которое команда зависит от изменений в ViewModel. В этом случае связь более или менее полностью содержится в ViewModel.

Итак, в приведенном выше примере в установщике для «ObjectName» вы должны вызвать RaiseCanExecuteChanged в команде «AddObjectCommand».

1
ответ дан 4 December 2019 в 21:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: