Please see the code examples. When starting up, the method CanExecuteButtonClick will be evaluated once. When executing the method CanExecuteButtonClick, the property CanClick is evaluated. I manually raise the CanExecuteChanged Event for the Command ButtonClick.
With a debugger I saw that the method RaiseCanExecuteChanged is called, but returns where the null-check if guard is implemented.
As a consequence, the UI is obviously not updated, as the event doesnt re-evaluate the according CanExecute method.
Has anyone encountered this problem yet, or can give me some feedback if I made a mistake with my implementation?
This is the (standard) implementation of ICommand, how I implemented it in my project. I implemented the RaiseCanExecuteChanged method inspired by this stackoverflow article.
public class RelayCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged == null) return;
CanExecuteChanged(this, EventArgs.Empty);
}
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
}
I implemented my commands like this: XAML:
<Button Content="Click me"
Command="{x:Bind ViewModel.ButtonClick}">
ViewModel:
public RelayCommand ButtonClick => new RelayCommand((obj) => ExecuteButtonClick(null), (obj) => CanExecuteButtonClick(null));
private async Task ExecuteButtonClick(object commandParameter)
{
//Some code
}
private bool CanExecuteButtonClick(object commandParameter) => CanClick;
//Another command, for simplicity only the execute method
private async Task ExecuteAnotherButtonClick(object commandParameter)
{
//Some code
CanClick = true;
ButtonClick.RaiseCanExecuteChanged();
}
Might not a straight answer to your question but let me show you some samlple code using the CommunityToolkit.Mvvm NuGet package, which comes with several source generators. You'll be able to avoid a bunch of boilerplate code:
// This class needs to be "partial" for the source generators.
public partial class SomeViewModel : ObservableObject
{
// The source generator will generate a "IsReady" property for you.
[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(DoSomethingCommand))]
private bool _isReady;
public bool CanDoSomething() => IsReady;
// The source generator will generate a "DoSomethingCommand" for you.
[RelayCommand(CanExecute = nameof(CanDoSomething))]
private async void DoSomething()
{
}
}
<ToggleSwitch IsOn="{x:Bind ViewModel.IsReady, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button
Command="{x:Bind ViewModel.DoSomethingCommand}"
Content="Do something" />