简体   繁体   English

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

[英]How to call ICommand.CanExecute in this case?

OK. 好。 Here's the scenario. 这是场景。 This is WPF + MVVM (.net 4.0) application: 这是WPF + MVVM(.net 4.0)应用程序:

  1. View: Has a DataGrid and two buttons Move Up / Move Down, which are supposed to move selected record up or down in the DataGrid . 视图:具有DataGrid和两个按钮上移/下移,这些按钮应该在DataGrid向上或向下移动选定的记录。 Both the grid and the buttons use XAML-based binding. 网格和按钮都使用基于XAML的绑定。
  2. ViewModel: Has a public property of DataView type to which my DataGrid will bind. ViewModel:具有DataView类型的公共属性,我的DataGrid将绑定到该属性。 Then there are two ICommand implementations listed below. 然后,下面列出了两个ICommand实现。 The two buttons will bind to these two commands. 这两个按钮将绑定到这两个命令。 Last but not the least there are two functions called MoveUp() and MoveDown() that do the obvious. 最后但并非最不重要的是,有两个名为MoveUp()MoveDown()函数可以做到这一点。
  3. Two ICommand implementations: In each command, CanExecute() returns whether selected record can be moved up or down respectively, and Execute() actually moves the record by calling ViewModel's MoveUp() and MoveDown() functions I described above. 两个ICommand实现:在每个命令中, CanExecute()返回所选记录是否可以分别向上或向下移动,并且Execute()实际上通过调用上面描述的ViewModel的MoveUp()MoveDown()函数来移动记录。 These command objects get a reference of VM object in their constructors. 这些命令对象在其构造函数中获取VM对象的引用。

First off, I want to know if this architecture correct and in line with MVVM pattern? 首先,我想知道这个架构是否正确并符合MVVM模式? Secondly, the problem at hand is that my buttons do not get enabled/disabled when I change selected record in the DataGrid , which brings up 2 sub-questions: 其次,当我在DataGrid更改所选记录时,我的按钮没有被启用/禁用,这带来了两个子问题:

  1. Who calls CanExecute() and when? 谁调用CanExecute()以及什么时候?
  2. How can I call it manually? 我怎样才能手动调用它? I have tried CommandManager.InvalidateRequerySuggested() after reading some other SO questions, but that didn't help either. 我在阅读了其他一些SO问题后尝试过CommandManager.InvalidateRequerySuggested() ,但这也无济于事。

Here's my CommandBase class, from which both my Command classes inherit: 这是我的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);
    }
}

I want to know if this architecture correct and in line with MVVM pattern? 我想知道这个架构是否正确并符合MVVM模式?

Yeah, that's completely in compliance with MVVM pattern. 是的,这完全符合MVVM模式。


Who calls CanExecute() and when? 谁调用CanExecute()以及什么时候?

CanExecute() gets called whenever CanExecuteChanged event is raised. 每当CanExecuteChanged事件时,都会调用CanExecute()。

Commands internally hooks to this event and enable/disable button or any frameworkElement based on bool property returned by CanExecute delegate . 命令内部挂钩此事件并启用/禁用按钮或任何基于CanExecute委托返回的bool属性的frameworkElement


How can I call it manually? 我怎样才能手动调用它?

First of all create RaiseCanExecuteChanged() method in your concrete implementation (if not already) so that it can be called manually. 首先在具体实现中创建RaiseCanExecuteChanged()方法(如果还没有),以便可以手动调用它。 This will look like this: 这将是这样的:

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

Since in your case, you need to call CanExecute() whenever SelectedItem in dataGrid changes. 因为在您的情况下,只要dataGrid中的SelectedItem发生更改,您就需要调用CanExecute() I would suggest to bind SelectedItem of DataGrid to some property in your ViewModel and in setter, you manually call RaiseCanExecuteChanged() so that CanExecute can be called on your command instance. 我建议将DataGrid的SelectedItem绑定到ViewModel中的某个属性,并在setter中手动调用RaiseCanExecuteChanged() ,以便可以在命令实例上调用RaiseCanExecuteChanged()


However, there is another way if you don't want to manually call RaiseCanExecuteChanged() method. 但是,如果您不想手动调用RaiseCanExecuteChanged()方法,还有另一种方法。 You can hook to CommandManager.RequerySuggested event which gets raised whenever CommandManager feels like that UI needs to be refreshed. 您可以挂钩CommandManager.RequerySuggested事件,只要CommandManager感觉需要刷新UI,就会引发该事件。

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

RequerySuggested event Occurs when the System.Windows.Input.CommandManager detects conditions that might change the ability of a command to execute. RequerySuggested事件在System.Windows.Input.CommandManager检测到可能更改命令执行能力的条件时发生。

So, whenever CommandManager.RequerySuggested gets raised, it will eventually raises your CanExecuteChanged thereby calling CanExecute of your command. 因此,每当引发CommandManager.RequerySuggested时,它最终会引发CanExecuteChanged从而调用命令的CanExecute Hence enable/disable button based on bool returned by CanExecute delegate. 因此,启用/禁用基于CanExecute委托返回的bool的按钮。

Replace your 'OnExcuteChanged' with this... 用这个替换'OnExcuteChanged'......

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

I personally always use the implementation RelayCommand from Josh Smith: 我个人总是使用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);
    }
}

Quote about CanExecutedChanged event from this link : 从这个链接引用CanExecutedChanged事件:

The CanExecuteChanged event, which is part of the ICommand interface implementation, has some interesting features. CanExecuteChanged事件是ICommand接口实现的一部分,具有一些有趣的功能。 It delegates the event subscription to the CommandManager.RequerySuggested event. 它将事件订阅委托给CommandManager.RequerySuggested事件。 This ensures that the WPF commanding infrastructure asks all RelayCommand objects if they can execute whenever it asks the built-in commands. 这可以确保WPF命令基础结构询问所有RelayCommand对象是否可以在它询问内置命令时执行。

In your case, this event did not work, and the object has not received information about the possibility of executing the command. 在您的情况下,此事件不起作用,并且对象未收到有关执行命令的可能性的信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM