【问题标题】:WPF: CanExecute is Always DisabledWPF:CanExecute 始终被禁用
【发布时间】:2015-05-08 17:39:04
【问题描述】:

我有一个文本框供用户输入字符串,还有一个“添加”按钮来执行一些带有该值的任务。我在 DelegateCommand 中添加了一个 CanExecute 方法,以便该按钮仅在框中有文本时才起作用。

但是,它始终处于禁用状态 - 即使条件为真。在框中键入不会启用按钮。

<TextBox Text="{Binding BuildEntryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding AddBuildCommand}" Content="Add" />

this.AddBuildCommand = new DelegateCommand(() => this.AddBuild(), () => this.CanAddBuild());

private bool CanAddBuild()
{
    if (this.BuildEntryText != null)
    {
        return true;
    }
    else
    {
        return false;
    }
}

我以前也遇到过这种情况(并不总是使用相同类型的控件),通常我会删除 CanExecute 并将其更改为操作方法中的“if”检查。不过,这一次,我实际上必须让它工作并以“正确”的方式将按钮变灰。

任何见解将不胜感激。提前致谢。

更新:

我已将 RaiseCanExecuteChanged 添加到 BuildEntryText 属性的设置部分。现在按钮开始禁用并在第一次输入文本时启用。但是,即使在删除文本或通过添加功能清除框后,它仍然处于启用状态。

public string BuildEntryText
{
    get
    {
        return this.buildEntryText;
    }

    set
    {
        this.SetProperty<string>(ref this.buildEntryText, value);
        this.AddBuildCommand.RaiseCanExecuteChanged();
    }
}

【问题讨论】:

  • 你有什么样的DelegateCommand?听起来它不会在发生任何更改通知时自动重新查询 CanExecuteChanged 。解决方案是在需要时手动提高 CanExecuteChanged like this,或者更改您的命令定义
  • @Rachel:我不知道 DelegateCommand 有不同的种类。我在 BuildEntryText 中的 SetProperty 调用之后添加了 RaiseCanExecuteChanged,现在当输入文本时该按钮变为启用状态。但是,删除文本时它不会禁用。
  • @Cricketheads,展示你的 DelegateCommand 实现。如果 CanExecuteChanged 没有正确实现,它不会通知绑定到命令的控件。
  • @ThomasLevesque:我不完全确定,但听起来你在问我在哪里添加了 RaiseCanExecuteChanged,我已将其附加到上述问题中。
  • @Cricketheads,不,我问的是你是如何实现CanExecuteChanged 事件的。在典型的实现中,您只需委托CommandManager.RequerySuggested 事件,该事件会自动引发。如果你这样做,你不需要手动调用RaiseCanExecuteChanged。

标签: wpf data-binding delegatecommand


【解决方案1】:

我发现这是一个由两部分组成的问题,实际上并不是我的 DelegateCommand 的问题(至少,不完全是)。

1) 我需要按照 Rachel 的建议调用 RaiseCanExecuteChanged,否则 UI 不会刷新控件的 IsEnabled 属性。

2) 我需要添加一个额外的条件来检查字符串的长度,而不是只检查它是否为空。显然,一个空的 TextBox 以 null 开头,但是如果您添加字符然后删除它们,它不会再次返回为 null。这是一个长度为 0 的字符串。

<TextBox Text="{Binding BuildEntryText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Command="{Binding AddBuildCommand}" Content="Add" />

public DelegateCommand AddBuildCommand { get; private set; }

this.AddBuildCommand = new DelegateCommand(() => this.AddBuild(), () => this.CanAddBuild());

public string BuildEntryText
{
    get
    {
        return this.buildEntryText;
    }

    set
    {
        this.SetProperty<string>(ref this.buildEntryText, value);

        // PART 1:
        this.AddBuildCommand.RaiseCanExecuteChanged();
    }
}

private bool CanAddBuild()
{
    // PART 2:
    if (this.BuildEntryText != null && this.BuildEntryText.Length > 0)
    {
        return true;
    }
    else
    {
        return false;
    }
}

【讨论】:

    【解决方案2】:

    您的委托命令需要重新查询以检查命令是否可以执行。委托命令定义了名为 CanExecuteChanged 的事件,该事件在 UI 线程上引发,以使每个调用控件重新查询以检查命令是否可以执行

    这就是委托命令的样子(你的也可能类似)

    public class DelegateCommand<T> : System.Windows.Input.ICommand
    {
       private readonly Predicate<T> _canExecute;
       private readonly Action<T> _execute;
    
         public DelegateCommand(Action<T> execute)
        : this(execute, null)
         {
         }
    
        public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        public bool CanExecute(object parameter)
        {
            if (_canExecute == null)
                return true;
    
            return _canExecute((parameter == null) ? default(T) :(T)Convert.ChangeType(parameter, typeof(T)));
        }
    
        public void Execute(object parameter)
        {
            _execute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
        }
    
        public event EventHandler CanExecuteChanged;
        public void RaiseCanExecuteChanged()
        {
            if (CanExecuteChanged != null)
                CanExecuteChanged(this, EventArgs.Empty);
        }
    }
    

    你将它绑定到TextBox的文本命令

    这是一个示例 ViewModel.cs

    public class ViewModel
    {
        private readonly DelegateCommand<string> MyButtonCommand;
    
        public ViewModel()
        {
            MyButtonCommand= new DelegateCommand<string>(
                (s) => { MessageBox.Show("Command Executed")}, //Execute
                (s) => { return !string.IsNullOrEmpty(_input); } //CanExecute
                );
        }
    
        public DelegateCommand<string> AddBuildCommand
        {
            get { return MyButtonCommand; }
        }
    
        private string _input;
        public string BuildEntryText
        {
            get { return _input; }
            set
            {
                _input = value;
                MyButtonCommand.RaiseCanExecuteChanged();
            }
        }
    }  
    

    并且在您的 XAML

    中
     <TextBox Text="{Binding BuildEntryText,   UpdateSourceTrigger=PropertyChanged}" />
    <Button Command="{Binding AddBuildCommand}" Content="Add" />
    

    【讨论】:

    • Kyle,我不确定我是否遵循您在这里的操作。使用 DelegateCommand,您是否通过命令将文本框的内容作为参数发送?如果是这样,您不需要按钮 xaml 中的 CommandParameter 属性吗?看起来你有两个单独的 DelegateCommands 正在运行。让一个返回另一个的目的是什么?
    • 是的,它将字符串作为参数传递,这是您的文本框内容。将来如果您必须将其绑定到 DateTime,您也可以传递它。在委托命令中,我使用了 Anonymous method,这实际上是您的这样做只是您可以说的捷径。为什么不在单独的 WPF 应用程序中运行此代码,它会让事情变得清晰
    猜你喜欢
    • 2013-09-05
    • 1970-01-01
    • 1970-01-01
    • 2013-03-02
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多