在这种情况下如何调用ICommand.CanExecute?

时间:2014-02-08 15:31:23

标签: c# .net wpf xaml mvvm

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

  1. 查看:有一个DataGrid和两个按钮上移/下移,这些按钮可以在DataGrid中向上或向下移动选定的记录。网格和按钮都使用基于XAML的绑定。
  2. ViewModel:具有DataView类型的公共属性,我的DataGrid将绑定到该属性。然后,下面列出了两个ICommand实现。这两个按钮将绑定到这两个命令。最后但并非最不重要的是,有两个名为MoveUp()MoveDown()的函数可以做到这一点。
  3. 两个ICommand实现:在每个命令中,CanExecute()返回所选记录是否可以分别向上或向下移动,Execute()实际上通过调用ViewModel的MoveUp()移动记录和我上面描述的MoveDown()函数。这些命令对象在其构造函数中获取VM对象的引用。
  4. 首先,我想知道这个架构是否正确并符合MVVM模式?其次,当我在DataGrid更改所选记录时,我的按钮无法启用/禁用,这会带来2个子问题:

    1. 谁致电CanExecute()以及何时?
    2. 如何手动调用?在阅读了其他一些SO问题之后,我尝试了CommandManager.InvalidateRequerySuggested(),但这也无济于事。
    3. 这是我的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);
          }
      }
      

3 个答案:

答案 0 :(得分:5)

  

我想知道这个架构是否正确并符合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()。我建议将DataGrid的SelectedItem绑定到ViewModel中的某个属性,并在setter中手动调用RaiseCanExecuteChanged(),以便可以在命令实例上调用CanExecute。


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

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

RequerySuggested事件发生时    System.Windows.Input.CommandManager 检测可能的情况   改变命令执行的能力。

因此,每当CommandManager.RequerySuggested被引发时,它最终会引发你的CanExecuteChanged,从而调用你的命令CanExecute。因此,根据CanExecute委托返回的bool启用/禁用按钮。

答案 1 :(得分:0)

用此替换'OnExcuteChanged'......

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

答案 2 :(得分:0)

我个人总是使用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);
    }
}

引用此linkCanExecutedChanged事件:

  

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

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