Reputation: 4248
Maybe the longest question title of all time! Because this is a two part question.
(1) I do not understand how setting NotifyOnValidationError="True" can trigger updates to my CanExecute. There is a bit of magic involved here that I need to understand. Someone(thing) subscribes to the CanExecuteChanged event of my ICommand but the call stack points to External code, so I can not figure out what is going on.
(2) Maybe the most important follow up questions is: Why does it not work in MVVM Light RelayCommand! The CanExecute is only called once at initialization and then never again. Looking at the source code for RelayCommand in MVVM Light does not reveal any chocking differences compared to my own implementation. I should mention that Prism's DelegateCommand does not seem to work either.
(Bonus) Maybee I am approaching this problem the wrong way? I just basically want to enable/disable buttons based on Validation failiures.
XAML (snippet):
<TextBox Grid.Column="1" Grid.Row="0">
<Binding Path="X" UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
<Binding.ValidationRules>
<ExceptionValidationRule></ExceptionValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox>
<Button Grid.Column="1" Grid.Row="3" Command="{Binding CalculateCommand}">
Calculate
</Button>
RelayCommand:
public class MyRelayCommand : ICommand
{
readonly Action<object> Execute_;
readonly Predicate<object> CanExecute_;
public MyRelayCommand(Action<object> Execute, Predicate<object> CanExecute)
{
if (Execute == null)
throw new ArgumentNullException("No action to execute for this command.");
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);
}
}
ViewModel:
private DelegateCommand _calculateCommmand;
public DelegateCommand CalculateCommand
{
get
{
return _calculateCommmand ?? (_calculateCommmand = new DelegateCommand(
() =>
{
Sum = X + X;
},
() =>
{
try
{
Convert.ChangeType(X, TypeCode.Byte);
return true;
}
catch
{
return false;
}
}));
}
}
PS: If you wanna buy my X + X program when it is done email me at [email protected]
Upvotes: 1
Views: 713
Reputation: 4248
(1) I think it goes like this.
I would like to know more about the third bullet point above, so I will keep the question open for a little longer to give the experts a chance to chime in.
Upvotes: 0
Reputation: 4248
(2) I figured this one out myself. You can choose to include RelayCommand from two different namespaces, make sure you use
using GalaSoft.MvvmLight.CommandWpf;
I am still looking for a good answer to (1), how the plumbing works that raises CanExecutetChanged based on validation error.
Upvotes: 1
Reputation: 3248
I don't think it depends on the ICommand
implementation. In yours, I see a public event EventHandler CanExecuteChanged
, where you tell the CommandManager
to handle the invocation of your command's CanExecute()
method. Without the CommandManager, you would have to handle this yourself, e.g. by providing your ICommand implementation with a public void RaiseCanExecuteChanged()
method that your ViewModel has to call for every command it considers to be needed to recalculated, e.g. inside the ViewModel's OnPropertyChanged
. Example: https://codereview.stackexchange.com/questions/124361/mvvm-am-i-doing-it-right
So the CommandManager does the magic for you. As soon as you invoke your ViewModel's PropertyChanged
event, the "external code" handles the affected commands and asks them for a fresh CanExecute()
value.
Upvotes: 0