Кнопка включения на основе значения TextBox (WPF) - PullRequest
9 голосов
/ 11 марта 2010

Это приложение MVVM.Есть окно и связанный класс модели представления.

В форме есть TextBox, Button и ListBox.Кнопка привязана к DelegateCommand с функцией CanExecute.Идея состоит в том, что пользователь вводит некоторые данные в текстовое поле, нажимает кнопку, и данные добавляются в список.

Я хотел бы включить команду (и кнопку), когда пользователь вводит правильные данные в TextBox.Теперь все работает так:

  • CanExecute() метод содержит код, который проверяет правильность данных в свойстве, привязанном к текстовому полю.
  • Текстовое поле привязано к свойству в модели представления
  • UpdateSourceTrigger установлен на PropertyChanged, и свойство в представлении модели обновляется после каждого нажатия пользователем клавиши.

Проблема в том, что CanExecute() не срабатывает, когда пользователь вводит данные в текстовое поле.Он не срабатывает даже тогда, когда текстовое поле теряет фокус.

Как я могу сделать эту работу?

Редактировать:
Re Комментарий Янко:
ДелегатКоманда реализована в шаблоне инструментария MVVM, и при создании нового проекта MVVM в решении есть команда Delegate.Насколько я видел в видео Prism, это должен быть тот же класс (или, по крайней мере, очень похожий).

Вот фрагмент кода XAML:

    ...
    <UserControl.Resources>
      <views:CommandReference x:Key="AddObjectCommandReference" 
                              Command="{Binding AddObjectCommand}" />
   </UserControl.Resources>

   ...
   <TextBox Text="{Binding ObjectName, UpdateSourceTrigger=PropertyChanged}"> </TextBox>
   <Button Command="{StaticResource AddObjectCommandReference}">Add</Button>
   ...

Просмотр модели:

   // 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() сработал в установщике ObjectName свойства из приведенного выше кода.

Ре Бен и Абе ответили:

  • CanExecuteChanged() - обработчик событий, и компилятор жалуется:

    Событие 'System.Windows.Input.ICommand.CanExecuteChanged' может появляться только слева от + = или - =

  • тамтолько два члена ICommand: Execute() и CanExecute()

У вас есть пример, показывающий, как я могу сделать командный вызов CanExecute().

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

В любом случае, идея для того, чтобы активировать команду на основе пользователяввод, нужно "подтолкнуть" объект команды в коде установщика свойства выглядит неуклюже.Он будет вводить зависимости, и одна из важных особенностей MVVM - их уменьшение.

Редактировать 2:

Я попытался активировать CanExecute, вызвав addObjectCommand.RaiseCanExecuteChanged() на ObjectName свойство setter из кода выше.Это тоже не помогает.CanExecute() запускается несколько раз, когда форма инициализируется, но после этого она никогда не выполняется снова.Это код:

   // Property bound to textbox
   public string ObjectName
    {
        get { return objectName; }
        set { 
            objectName = value;
            addObjectCommand.RaiseCanExecuteChanged();              
            OnPropertyChanged("ObjectName");
        }
    }

Редактировать 3: Решение

Как Янко Янков и JerKimball написал, проблемаэто статический ресурс.Когда я изменил привязку кнопки, как предложил Янко:

<Button Command="{Binding AddObjectCommand}">Add</Button>

все сразу заработало.Мне даже не нужно RaiseCanExecuteChanged().* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *] * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 11 * 11 * * 11 11 * .Пример в этом руководстве определяет команды как статический ресурс, а затем привязывает его к пункту меню.Разница в том, что вместо строкового свойства в моем примере руководство MVVM работает с ObservableCollection.

Редактировать 4: Окончательное объяснение

Я наконец получил его.Все, что мне нужно было сделать, это прочитать комментарий в классе CommandReference.Он говорит:

/// <summary>
/// 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.
/// </summary>

Итак, CommandReference используется для KeyBinding, это не для связывания в визуальных элементах.В приведенном выше коде ссылки на команды, определенные в ресурсах, будут работать для связывания ключей, которого у меня нет в этом пользовательском элементе управления.
Конечно, пример кода, поставляемый с WPF MVVM toolkit, был верным, но я неправильно его прочитал и использовал CommandReference в связывании визуальных элементов.
Этот WPF MVVM иногда бывает сложным.

Ответы [ 5 ]

4 голосов
/ 12 марта 2010

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

<Button Command="{Binding AddObjectCommand}">Add</Button>
3 голосов
/ 12 марта 2010

Поскольку вы используете DelegateCommand, вы можете вызывать его метод RaiseCanExecuteChanged при изменении вашего свойства текста. Я не уверен, что вы пытаетесь выполнить с помощью ресурса CommandReference, но обычно вы просто привязываете команды непосредственно к свойству Command элемента button:

<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");
    }
}
1 голос
/ 11 октября 2018

Я знаю, что это старый вопрос, но лично я думаю, что проще связать текстовое поле Length со свойством кнопки IsEnabled, например ::

<TextBox Name="txtbox" Width="100" Height="30"/>
<Button Content="SomeButton " Width="100" Height="30" 

  IsEnabled="{Binding ElementName=txtbox, Path=Text.Length, Mode=OneWay}"></Button>
1 голос
/ 12 марта 2010

Здесь повторяется Абэ, но «правильный» путь, который можно использовать здесь, использует:

public void RaiseCanExecuteChanged();

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

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

1 голос
/ 11 марта 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...