I’m using the current version of MvvmLight available on Nuget (4.1.23.0) and calling RaiseCanExecuteChanged does not appear to be doing anything in a unit test. The scenario is very simple, I have a command:
public RelayCommand FooCommand { get; private set; }
I new it up in the view model constructor and point it to some private methods:
FooCommand = new RelayCommand(Foo, CanFoo);
private void Foo()
{
// do some fooing.
}
private bool CanFoo()
{
return SomeRequiredProperty != null;
}
Then in the setter for SomeRequiredProperty I call RaiseCanExecuteChanged:
public object SomeRequiredProperty
{
get
{
return someRequiredProperty;
}
set
{
someRequiredProperty = value;
FooCommand.RaiseCanExecuteChanged();
}
}
Now in a unit test I do the following:
// Arrange
var canExecuteChanged = false;
viewModel.FooCommand.CanExecuteChanged += (sender, args) => canExecuteChanged = true;
// Act
viewModel.SomeRequiredProperty = new object();
// Assert
Assert.That(canExecuteChanged, Is.True);
The test fails because my event handler is not firing. Why is that?
Update: The behaviour does indeed work at run time.
Fixed!
nemesv was correct in that
FooCommand.RaiseCanExecuteChanged()simply callsCommandManager.InvalidateRequerySuggested().In addition to that,
FooCommand.CanExecuteChangedsimply forwards the handler on to theCommandManager.RequerySuggestedevent:The cause of the problem was the following line of code in the CommandManager class:
This line places a work item with DispatcherPriority
Backgroundon the Dispatcher work item queue. The work item is supposed to notify all handlers of theCommandManager.RequerySuggestedevent.The problem is that this work item is never run.
The solution is to force the dispatcher to run the work item.
I found the solution in this discussion on the MVVM Foundation CodePlex page. I managed to simplify the code somewhat into the following helper class.
My test now looks like this:
And, most importantly, it passes 🙂