OK. Here's the scenario. This is WPF + MVVM (.net 4.0) application:
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.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.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:
CanExecute()
and when?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);
}
}
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.
Replace your 'OnExcuteChanged' with this...
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With