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

C# 在这种情况下,如何调用ICommand.CanExecute?,c#,.net,wpf,xaml,mvvm,C#,.net,Wpf,Xaml,Mvvm,嗯。下面是一个场景。这是WPF+MVVM(.net 4.0)应用程序: 视图:有一个DataGrid和两个向上/向下移动按钮,用于在DataGrid中向上或向下移动所选记录。网格和按钮都使用基于XAML的绑定 ViewModel:具有一个公共属性,其类型为DataView,myDataGrid将绑定到该属性。下面列出了两个ICommand实现。这两个按钮将绑定到这两个命令。最后但并非最不重要的是,有两个函数名为MoveUp()和MoveDown() 两个ICommand实现:在每个命令中,Can

嗯。下面是一个场景。这是WPF+MVVM(.net 4.0)应用程序:

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

  • 谁调用
    CanExecute()
    以及何时调用
  • 我如何手动调用它?在阅读了其他一些SO问题后,我尝试了
    CommandManager.invalidateRequestSuggested()
    ,但也没有任何帮助
  • 这是我的
    CommandBase
    类,我的两个命令类都从中继承:

    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);
        }
    }
    

    将您的“OnExcuteChanged”替换为此

    public event EventHandler CanExecuteChanged
    {
        add
        {
            CommandManager.RequerySuggested += value;
        }
        remove
        {
            CommandManager.RequerySuggested -= value;
        }
    }
    
    我想知道这个架构是否正确,是否符合MVVM 模式

    是的,这完全符合MVVM模式


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

    每当引发
    CanExecuteChanged
    事件时,都会调用CanExecute()

    命令在内部钩住此事件,并根据CanExecute委托返回的bool属性启用/禁用按钮或任何frameworkElement


    我如何手动调用它

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

        public void RaiseCanExecuteChanged()
        {
            EventHandler canExecuteChangedHandler = CanExecuteChanged;
            if (canExecuteChangedHandler != null)
            {
               canExecuteChangedHandler(this, EventArgs.Empty);
            }
        }
    
    因为在您的情况下,只要dataGrid中的SelectedItem发生更改,就需要调用
    CanExecute()
    。我建议将DataGrid的
    SelectedItem
    绑定到ViewModel中的某个属性,并在setter中手动调用
    raiseCanecuteChanged()
    ,以便可以在命令实例上调用CanExecute


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

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
    System.Windows.Input.CommandManager检测可能导致以下情况的条件 更改命令的执行能力


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

    我个人总是使用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);
        }
    }
    
    内部类RelayCommand:ICommand
    {
    私有只读操作\u执行;
    私有只读谓词_canExecute;
    public RelayCommand(Action execute):这个(execute,null)
    {
    }
    公共RelayCommand(操作执行,谓词canExecute)
    {
    if(execute==null)
    {
    抛出新的ArgumentNullException(“执行”);
    }
    _执行=执行;
    _canExecute=canExecute;
    }
    公共布尔CanExecute(对象参数)
    {
    返回_canExecute==null?true:_canExecute(参数);
    }
    公共事件事件处理程序CanExecuteChanged
    {
    添加
    {
    CommandManager.RequerySuggested+=值;
    }
    去除
    {
    CommandManager.RequerySuggested-=值;
    }
    }
    public void Execute(对象参数)
    {
    _执行(参数);
    }
    }
    
    引用以下内容中有关
    CanExecutedChanged
    事件的内容:

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


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

    您使用的是哪个版本的
    ICommand
    ?您所说的版本是什么意思?
    System.Windows.Input
    (PresentationCore.dll 4.0.0)中只有一个
    ICommand
    。是的,我的意思是
    DelegateCommand
    RelayCommand
    或您自己派生的
    ICommand
    ?mmm…不确定。我正在粘贴上面的
    CommandBase
    类。我所有的具体命令类都继承自它。
    CanExecute
    方法总是从基类方法返回true。我希望您在具体的命令实现中将其设置为实际的
    谓词
    ,否则命令
    CanExecute
    willa始终返回true,按钮将永远不会被禁用。超级!你也能输入一些解释吗?谢谢你的详细输入。有一个问题:
    raisecancecutechanged()
    被标记为受保护。我的ViewModel将无法访问它。没有?对,我只是复制了你的
    OnCanExecuteChanged()
    。应该是公共的