【问题标题】:How to call ICommand.CanExecute in this case?在这种情况下如何调用 ICommand.CanExecute?
【发布时间】:2014-02-08 15:31:23
【问题描述】:

好的。这是场景。这是 WPF + MVVM (.net 4.0) 应用程序:

  1. 视图:有一个DataGrid 和两个按钮Move Up / Move Down,它们应该在DataGrid 中向上或向下移动选定的记录。网格和按钮都使用基于 XAML 的绑定。
  2. ViewModel:有一个DataView 类型的公共属性,我的DataGrid 将绑定到该属性。然后下面列出了两个ICommand 实现。这两个按钮将绑定到这两个命令。最后但同样重要的是,有两个函数称为 MoveUp()MoveDown(),它们的作用显而易见。
  3. 两个ICommand实现:在每个命令中,CanExecute()分别返回选择的记录是否可以向上或向下移动,Execute()实际上是通过调用ViewModel的MoveUp()MoveDown()函数来移动记录我上面描述的.这些命令对象在其构造函数中获取 VM 对象的引用。

首先,我想知道这个架构是否正确并符合 MVVM 模式?其次,手头的问题是,当我更改 DataGrid 中的选定记录时,我的按钮没有启用/禁用,这带来了 2 个子问题:

  1. 谁打电话给CanExecute(),什么时候打电话?
  2. 如何手动调用它?在阅读了其他一些 SO 问题后,我尝试了CommandManager.InvalidateRequerySuggested(),但这也无济于事。

这是我的 CommandBase 类,我的两个 Command 类都继承自该类:

internal abstract class CommandBase : DependencyObject, ICommand
{
    public virtual bool CanExecute(Object parameter)
    {
        return true;
    }

    public abstract void Execute(Object parameter);
    public event EventHandler CanExecuteChanged;

    protected void OnCanExecuteChanged(Object sender, EventArgs e)
    {
        CanExecuteChanged(sender, e);
    }
}

【问题讨论】:

  • 您使用的是哪个版本的ICommand
  • 版本是什么意思? System.Windows.Input (PresentationCore.dll 4.0.0) 中只有一个 ICommand
  • 是的,我的意思是 DelegateCommandRelayCommand 或您自己的 ICommand 派生实现?
  • 嗯...不确定。我在上面粘贴我的CommandBase 类。我所有的具体命令类都继承自它。
  • CanExecute 方法总是从基类方法返回 true。我希望您在具体的命令实现中将其设置为实际的Predicate,否则命令CanExecute 将始终返回true,并且永远不会禁用按钮。

标签: c# .net wpf xaml mvvm


【解决方案1】:

我想知道这个架构是否正确并符合 MVVM 模式?

是的,这完全符合 MVVM 模式。


谁调用 CanExecute() 以及何时调用?

只要引发CanExecuteChanged 事件,就会调用CanExecute()。

命令在内部挂钩到此事件和启用/禁用按钮或任何基于 CanExecute 委托返回的 bool 属性的 frameworkElement


如何手动调用?

首先在您的具体实现中创建RaiseCanExecuteChanged() 方法(如果还没有),以便可以手动调用它。这将如下所示:

    public void RaiseCanExecuteChanged()
    {
        EventHandler canExecuteChangedHandler = CanExecuteChanged;
        if (canExecuteChangedHandler != null)
        {
           canExecuteChangedHandler(this, EventArgs.Empty);
        }
    }

因为在您的情况下,您需要在 dataGrid 中的 SelectedItem 更改时调用CanExecute()。我建议将 SelectedItem 的 DataGrid 绑定到您的 ViewModel 和 setter 中的某个属性,您手动调用 RaiseCanExecuteChanged() 以便可以在您的命令实例上调用 CanExecute。


但是,如果您不想手动调用RaiseCanExecuteChanged() 方法,还有另一种方法。您可以挂钩到 CommandManager.RequerySuggested 事件,只要 CommandManager 感觉需要刷新 UI,就会引发该事件。

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

RequerySuggested 事件 System.Windows.Input.CommandManager 检测可能会 更改命令的执行能力。

所以,每当CommandManager.RequerySuggested 被提升时,它最终会提升您的CanExecuteChanged 从而调用您的命令的CanExecute。因此,根据 CanExecute 委托返回的布尔值启用/禁用按钮。

【讨论】:

  • 感谢您的详细输入。一个问题:RaiseCanExecuteChanged() 被标记为受保护。我的 ViewModel 将无法访问它。没有?
  • 对,我只是复制了你的OnCanExecuteChanged()。它应该是公开的,也可以是内部的,以最适合您的方式。在答案中更新。
  • 再次感谢。我最终得到了CommandManager.RequerySuggested += value; 的东西。它工作得很好。不能接受多个答案,但绝对值得 +1。
  • 请注意,CommandManager.RequerySuggested += value; 会在您仍然不想时被最频繁地调用。 (可能是任何按钮单击、失去焦点或任何 GUI 交互)。休息绝对是你的选择。!!
  • 多年后才访问过这个。现在我对 WPF 有了更深入的了解,我觉得这个应该标记为正确答案。
【解决方案2】:

用这个替换你的“OnExcuteChanged”...

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

【讨论】:

  • 超级!你也可以输入一些解释吗?
【解决方案3】:

我个人一直使用来自 Josh Smith 的实现 RelayCommand

internal class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;

    public RelayCommand(Action<object> execute) : this(execute, null)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null ? true : _canExecute(parameter);
    }

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

        remove
        {
            CommandManager.RequerySuggested -= value;
        }
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

从此link引用CanExecutedChanged事件:

CanExecuteChanged 事件是 ICommand 接口实现的一部分,它有一些有趣的特性。它将事件订阅委托给 CommandManager.RequerySuggested 事件。这可确保 WPF 命令基础结构在询问内置命令时询问所有 RelayCommand 对象是否可以执行。

在您的情况下,此事件不起作用,并且对象尚未收到有关执行命令可能性的信息。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2015-11-30
    • 2017-05-16
    • 2012-10-06
    • 2011-05-18
    • 2019-11-29
    • 2022-01-23
    相关资源
    最近更新 更多