当条件满足时,CanExecute()未启用按钮

4

我有一个非常简单的应用程序,其中包括一个TextBox和一个Button。当输入到TextBox的文本长度超过5个字符时,按钮将被启用。以下是我的ViewModel代码:

private string _text { get; set; }
public string Text
{
    get { return _text; }
    set
    {
        _text = value;
        OnPropertyChanged("Text");
    }
}

private ICommand _buttonCommand;
public ICommand ButtonCommand
{
    get
    {
        if (_buttonCommand == null)
        {
            _buttonCommand = new RelayCommand(
                param => this.ButtonCommandExecute(), 
                param => this.ButtonCommandCanExecute()
            );
        }
        return _buttonCommand;
    }
}

private bool ButtonCommandCanExecute()
{
    if (this.Text.Length < 5)
    {
        return false;
    }
    else
    {
        return true;
    }
}

private void ButtonCommandExecute()
{
    this.Text = "Text changed";
}

public MainWindowViewModel()
{
    //
}
TextBoxButton 通过以下 XAML 进行绑定:
<Button Content="Button" HorizontalAlignment="Left" 
                Margin="185,132,0,0" VerticalAlignment="Top" Width="120"
                Command="{Binding Path=ButtonCommand}" />

<TextBox HorizontalAlignment="Left" Height="23" 
         Margin="185,109,0,0" TextWrapping="Wrap" 
         Text="{Binding Path=Text, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>

DataContext 看起来已经正确设置了,但这只是因为我是一个 WPF 的初学者。

private MainWindowViewModel view_model;

public MainWindow()
{
    InitializeComponent();

    view_model = new MainWindowViewModel();

    this.DataContext = view_model;
}

当我在TextBox中输入时,Button从未启用。

4
如果您切换到文本框之外,它是否正确启用/禁用?TextBox.Text的默认绑定模式是OnLostFocus,因此在TextBox失去焦点之前,数据不会回传到您的VM。要更改这个行为,可以将绑定Mode属性设置为PropertyChanged。另外,您正在使用哪种类型的RelayCommand?如果是MVVM light中的Relay命令,则应自动引发CanExecuteChanged并重新查询CanExecute,每当属性更改时。然而,并非所有的Relay命令都是这样的。 - Rachel
那也是个问题,非常感谢 Rachel。 - user3761858
3个回答

14

一些实现ICommand接口的类有特殊的方法来通知是否已更改"CanExecute"。例如,RelayCommand类(MVVM Light)就有这样的方法。

private string _text;
public string Text
{
    get { return _text; }
    set
    {
        _text = value;
        OnPropertyChanged("Text");

        // There is a special RelayCommand method to notify "CanExecute" changed.
        // After this call, the "CanExecute" state is "re-evaluated" automatically by binding using CanExecute Func passed into RelayCommand constructor.
        _buttonCommand.RaiseCanExecuteChanged();
    }
}

private RelayCommand _buttonCommand;
public ICommand ButtonCommand
{
    get
    {
        if (_buttonCommand == null)
        {
            _buttonCommand = new RelayCommand(
                param => this.ButtonCommandExecute(), 
                param => this.ButtonCommandCanExecute()
            );
        }
        return _buttonCommand;
    }
}

这个问题可能会有用:What is CanExecuteChanged for?


5
这是正确的答案,因为它可以使 ICommand 的设计正常工作,而不需要额外的属性或绑定。 - TyCobb

2
实际上,您需要创建一个布尔属性以绑定到按钮控件的IsEnabled属性。当您在文本框中输入的文本超过五个字符时,将此属性设置为true-您必须在Text属性的Setter中执行此操作,因为这是您在TextBox中键入时所调用的内容。
关于命令的基础知识:这些基本上是报告例如单击事件到C#代码(例如Viewmodel/Page.cs)的方式,以便您可以执行某些任务。它与启用和禁用按钮无关。
请按以下代码操作:
private string _text { get; set; }
public string Text
{
    get { return _text; }
    set
    {
        _text = value;

        if(_text.Length  > 5)
        // Enable button here
        // and command does not enable Buttons they are basically report the clicks events.
        IsButtonEnabled = true;

        OnPropertyChanged("Text");
    }
}

为启用按钮,创建一个名为IsButtonEnabled的Bool类型属性,并在Xaml中将此属性绑定到您的按钮。请保留HTML标签。
private bool _IsButtonEnabled { get; set; }
public bool IsButtonEnabled
{
    get { return _IsButtonEnabled ; }
    set
    {
        _IsButtonEnabled = value;
        OnPropertyChanged("IsButtonEnabled");
    }
}

在Xaml中:-
<Button Content="Button" HorizontalAlignment="Left" 

             IsEnabled="{Binding IsButtonEnabled}"

            Margin="185,132,0,0" VerticalAlignment="Top" Width="120"
            Command="{Binding Path=ButtonCommand}" />

兄弟,非常感谢!我按照你说的做了,因为这很有道理,但是你能详细说明一下IsButtonEnabled的绑定吗?我应该将其绑定到我的按钮的IsEnabled属性上吗? - user3761858
@user3761858 是的兄弟。我已经在答案底部添加了代码。 :) - loop
3
注意:不需要额外的属性。将命令(字段)存储为 RelayCommand,并在 OnPropertyChanged("Text") 后调用 RelayCommand.RaiseCanExecuteChanged(); - Sergey Vyacheslavovich Brunov
@SergeyBrunov 你的意思是说,如果按钮之前被禁用了,它将使其启用? - loop
@loop,是的,因为“Button”侦听[ICommand.CanExecuteChanged Event](http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.canexecutechanged%28v=vs.100%29.aspx)。请查看我的答案。 - Sergey Vyacheslavovich Brunov
1
@SergeyBrunov 谢谢,我不知道这个 :) 我会试一下,现在就去睡觉了。我现在正在用我的平板电脑。 - loop

-2

尝试一下对你的代码进行小修改,然后告诉我它是否有效:

private string _text { get; set; }
public string Text
{
    get { return _text; }
    set
    {
        _text = value;
        OnPropertyChanged("Text");
        ButtonCommandCanExecute();
    }
}

private ICommand _buttonCommand;
public ICommand ButtonCommand
{
    get
    {
        if (_buttonCommand == null)
        {
            _buttonCommand = new RelayCommand(
                param => this.ButtonCommandExecute(), 
                param => this.ButtonCommandCanExecute()
            );
        }
        return _buttonCommand;
    }
}

private bool ButtonCommandCanExecute()
{
    if (this.Text.Length < 5)
    {
        return false;
    }
    else
    {
        return true;
    }
}

private void ButtonCommandExecute()
{
    this.Text = "Text changed";
}

public MainWindowViewModel()
{
    //
}

1
这不会起作用:启用/禁用按钮的代码在哪里? - loop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接