WPF MVVM - простая кнопка привязки с текстовым полем (Icommand)

Я пытаюсь сделать следующее: когда текстовое поле содержит значение "123", оно должно активировать кнопку и позволить мне щелкнуть ее.

Решение Image & View Image

Я не могу найти способ инициировать команду кнопки (класс SpecialCommand.cs) на основе моих параметров кнопки. Не могли бы вы поддержать, где я неправильно понимаю этот шаблон MVVM?

WPF View [MainWindow.xaml]:

<Window.Resources>
    <ViewModel:MainWindowVM x:Key="WindowVm"></ViewModel:MainWindowVM>
</Window.Resources>

<Grid>
    <StackPanel>
        <TextBox x:Name="textBox" Margin="0, 5" Text="123"/>
        <Button Content="Click me!" Margin="0, 5" Command="{Binding SpecialCommand, Source={StaticResource WindowVm}}" CommandParameter="{Binding Text, ElementName=textBox, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
    </StackPanel>
</Grid>

ViewModel [MainWindowVM.cs]:

public class MainWindowVM
{
    private SpecialCommand _specialCommand;

    public SpecialCommand SpecialCommand { get => _specialCommand; set => _specialCommand = value; }

    public MainWindowVM()
    {
        _specialCommand = new SpecialCommand();
    }

}

Команда [SpecialCommand.cs]

public class SpecialCommand : ICommand
{
    public bool CanExecute(object parameter)
    {
        if (parameter != null && (parameter as string) == "123")
            return true;

        return false;
    }

    public void Execute(object parameter)
    {
        MessageBox.Show("Button Pressed!");
    }

    public event EventHandler CanExecuteChanged;
}

Я верю, что, может быть, я ошибаюсь, поскольку Button и Textbox находятся в представлении. Мне не нужно добавлять / изменять какие-либо методы в моей реализации SpecialCommand. Они должны иметь возможность видеть, когда свойство изменяется. Как и CanExecuteChanged() ниже, эта команда многократно вызывается и кажется излишней для этой небольшой задачи.

 public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

2 ответа

Вам нужен способ сообщить команде, что она должна проверить, CanExecute, Это делается путем запуска события CanExecuteChanged, Он сообщает кнопке запросить CanExecute имущество.

Чтобы это работало, я бы добавил текстовое свойство к вашей модели представления и привязал к нему текстовое поле.

В SpecialCommand добавить метод:

public void TriggerCanExecuteChanged()
{
    CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}

В классе MainWindowVM добавить свойство:

private string _text;
public string Text
{
    get { return _text; }
    set {
        if (value != _text) {
            _text = value;
            _specialCommand.TriggerCanExecuteChanged();
        }
    }
}

Посмотреть модель для реализации INotifyPropertyChanged (см. комментарии):

public class MainWindowVM : INotifyPropertyChanged
{
    public SpecialCommand SpecialCommand { get; set; } = new SpecialCommand();

    private string _text;
    public string Text
    {
        get { return _text; }
        set {
            if (value != _text) {
                _text = value;
                OnPropertyChanged(nameof(Text));
                SpecialCommand.TriggerCanExecuteChanged();
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Нет необходимости делать это сложнее, чем нужно.

public class MainWindowVM
{
    private string m_OneTwoThree;
    public string OneTwoThree{
        get { return OneTwoThree; }
        set {
            if (m_OneTwoThree != value){
                m_OneTwoThree = value;
                NotifyPropertyChanged(nameof(OneTwoThree)); //if you need this let me know
            }
        }
    }       

    public MainWindowVM()
    {

    }


    public ICommand RandomCommand { get { return new RelayCommand(OnRandom, IsOneTwoThree); } }


    private void OnRandom()
    {
        //do stuff
    }

    private bool IsOneTwoThree(){
        if (OneTwoThree == "123"){
            return true;
        }
        return false;
    }
}

Вам также придется обновить свой xaml Я не думаю, что он сможет найти 'OneTwoThree', поэтому вам придется связывать его самостоятельно, как обычно.

<StackPanel>
    <TextBox x:Name="textBox" Margin="0, 5" Text="{Binding OneTwoThree}"/>
    <Button Content="Click me!" Margin="0, 5" Command="{Binding RandomCommand, Source={StaticResource WindowVm}}"/>
</StackPanel>

Если у вас есть какие-либо вопросы просто спросить.

Это мой RelayCommand: используйте RelayCommand ("вещь для выполнения", "если функция возвращает true, вы можете выполнить");

public class RelayCommand : ICommand
{
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    private Action methodToExecute;
    private Func<bool> canExecuteEvaluator;
    public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
    {
        this.methodToExecute = methodToExecute;
        this.canExecuteEvaluator = canExecuteEvaluator;
    }
    public RelayCommand(Action methodToExecute)
        : this(methodToExecute, null)
    {
    }
    public bool CanExecute(object parameter)
    {
        if (this.canExecuteEvaluator == null) {
            return true;
        }
        else {
            bool result = this.canExecuteEvaluator.Invoke();
            return result;
        }
    }
    public void Execute(object parameter)
    {
        this.methodToExecute.Invoke();
    }
}
Другие вопросы по тегам