Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call ICommand.CanExecute in this case?

Tags:

c#

.net

mvvm

wpf

xaml

OK. Here's the scenario. This is WPF + MVVM (.net 4.0) application:

  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. Both the grid and the buttons use XAML-based binding.
  2. ViewModel: Has a public property of DataView type to which my DataGrid will bind. Then there are two ICommand implementations listed below. 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.
  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. These command objects get a reference of VM object in their constructors.

First off, I want to know if this architecture correct and in line with MVVM pattern? 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:

  1. Who calls CanExecute() and when?
  2. How can I call it manually? I have tried CommandManager.InvalidateRequerySuggested() after reading some other SO questions, but that didn't help either.

Here's my CommandBase class, from which both my Command classes inherit:

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);
    }
}
like image 577
dotNET Avatar asked Feb 08 '14 15:02

dotNET


2 Answers

I want to know if this architecture correct and in line with MVVM pattern?

Yeah, that's completely in compliance with MVVM pattern.


Who calls CanExecute() and when?

CanExecute() gets called whenever CanExecuteChanged event is raised.

Commands internally hooks to this event and enable/disable button or any frameworkElement based on bool property returned by CanExecute delegate.


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. 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. 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.


However, there is another way if you don't want to manually call RaiseCanExecuteChanged() method. You can hook to CommandManager.RequerySuggested event which gets raised whenever CommandManager feels like that UI needs to be refreshed.

    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.

So, whenever CommandManager.RequerySuggested gets raised, it will eventually raises your CanExecuteChanged thereby calling CanExecute of your command. Hence enable/disable button based on bool returned by CanExecute delegate.

like image 64
Rohit Vats Avatar answered Nov 09 '22 00:11

Rohit Vats


Replace your 'OnExcuteChanged' with this...

public event EventHandler CanExecuteChanged
{
    add
    {
        CommandManager.RequerySuggested += value;
    }
    remove
    {
        CommandManager.RequerySuggested -= value;
    }
}
like image 30
Sankarann Avatar answered Nov 08 '22 22:11

Sankarann