【问题标题】:Button stays disabled - DelegateCommand not re-evaluating CanExecute handler按钮保持禁用状态 - DelegateCommand 未重新评估 CanExecute 处理程序
【发布时间】:2016-12-20 06:19:02
【问题描述】:

问题:按钮永远无法启用。

<Button Name="btnCompareAxises"Command="{Binding CompareCommand}"
          Content="{Binding VM.CompareAxisButtonLabel}" 
        IsEnabled="{Binding VM.IsCompareButtonEnabled}">
</Button>

ViewModel 构造函数:

 this.CompareCommand = new DelegateCommand(CompareCommand, ValidateCompareCommand);

问题似乎与按钮的注册Command的CanExecute事件处理程序有关。 CanExecute 处理程序在应用程序加载时返回 false。 这很好,因为最初没有满足条件。

canExecute 处理程序仅在应用程序启动或单击按钮时运行。您不能单击禁用的按钮,因此如果从 CanExecute 处理程序返回的初始值为 false,则该按钮将永远保持禁用状态!

问题:
我是否必须再次启用该按钮,仅使用绑定到它的命令。 像,嘿命令,请重新评估此按钮的条件是否满足?

为什么 IsEnabled 属性位于 Coercion 部分而不是 local 部分?

命令:

public class DelegateCommand : ICommand
{
    private readonly Func<object, bool> canExecute;
    private readonly Action<object> execute;

    public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        this.OnCanExecuteChanged();
    }

    protected virtual void OnCanExecuteChanged()
    {
        var handler = this.CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

【问题讨论】:

  • 如果您已经绑定到通过 CanExecute 实现类似行为的命令,是否需要绑定 IsEnabled?
  • 不,我现在去掉了,按钮会自动禁用,这里不需要额外绑定。谢谢!

标签: wpf mvvm binding


【解决方案1】:

已解决:

我必须调整 DelegateCommand 类以使其工作:

我已将CommandManager.RequerySuggested 添加到公共CanExecuteChanged 事件属性中。

现在它会在 UI 发生变化时自动重新评估命令的 CanExecute 方法!

public class DelegateCommand : ICommand
{
    private readonly Func<object, bool> canExecute;
    private readonly Action<object> execute;

    public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    /// CommandManager
    /// Go to the "References" part of your class library and select "Add Reference". 
    /// Look for an assembly called "PresentationCore" and add it.
    public event EventHandler CanExecuteChanged
    {
        add
        {
            _internalCanExecuteChanged += value;
          CommandManager.RequerySuggested += value;

        }
        remove
        {
            _internalCanExecuteChanged -= value;
            CommandManager.RequerySuggested -= value;
        }
    }

    event EventHandler _internalCanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        this.OnCanExecuteChanged();
    }

    protected virtual void OnCanExecuteChanged()
    {
        var handler = this._internalCanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

从按钮中删除了这个:

 IsEnabled="{Binding VM.IsCompareButtonEnabled}"

这里的绑定不是必需的,因为 CanExecute 处理程序会处理按钮的启用/禁用状态!

【讨论】:

  • CommandManager 确实不是这样的最佳解决方案,因为不是一个按钮检查它们是否可以执行,而是 所有按钮都可以。您将阻塞 UI 线程,直到它们都执行检查,因此您拥有的线程越多,它们重新评估其状态所需的时间越长,您的 UI 被锁定的时间就越长。最好让您的 ICommand 公开一个可以触发 CanExecuteChanged 事件的方法。拥有的 ViewModel 应该确切地知道该命令何时可用,并且可以在它可以运行时向其观察者发出信号。
  • 当然,我可以让它变得超级简单,并且只在按钮单击和显示消息时验证 GUI :-) 这是一个小应用程序,所以它不会造成太大的伤害。但好点!综上所述,不要将 CommandManager 与 CanExecute 结合使用。人们应该在需要时手动调用它。
猜你喜欢
  • 2011-02-28
  • 2014-09-04
  • 2012-08-19
  • 1970-01-01
  • 1970-01-01
  • 2021-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多