Jim
Jim

Reputation: 2828

ICommand CanExecuteChanged is always null

I have a basic MVVM WPF app with a View, consisting of a texbox and submit button. Both controls are correctly binded to property and a command within ViewModel. The issue is that the CanSubmit is not triggered because CanExecuteChanged eventhandler (in DelegateCommand) is always null. Basically the question is how to properly notify Command to run CanExecute check when textox is updated.

public DelegateCommand  SubmitCommand => new DelegateCommand(Submit, CanSubmit);

private string _company;
public string Company
 {
     get => _company;
     set
     {
         SetProperty(ref _company, value);
         SubmitCommand.RaiseCanExecuteChanged();
      }
  }

My delegate command

public class DelegateCommand : ICommand
{
    private readonly Predicate<object> _canExecute;
    private readonly Action<object> _execute;

    public event EventHandler CanExecuteChanged;

    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public DelegateCommand(Action<object> execute) : this(execute, null) { }

    public virtual bool CanExecute(object parameter)
    {
        if (_canExecute == null)
        {
            return true;
        }

        return _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        if(CanExecuteChanged != null) <------ Always null
            CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

Upvotes: 0

Views: 947

Answers (2)

EldHasp
EldHasp

Reputation: 7918

I'll complement @lidqy answer.
He is perfectly correct that the problem is with the creation of a new command every time the property is accessed.
The instance of the command must always be the same.

More often than not, Submit and CanSubmit will be instance methods.
BUT the field (property) initializer can only access static members.

There are two typical methods for initializing a command:

  1. Initialization in the ViewModel constructor:
        public DelegateCommand SubmitCommand { get; }

        public ViewModel()
        {
            SubmitCommand = new DelegateCommand(Submit, CanSubmit);
        }
  1. Initialization at the first call to the property:
    private DelegateCommand _submitCommand;

    public DelegateCommand SubmitCommand => _submitCommand
        ?? (_submitCommand = new DelegateCommand(Submit, CanSubmit));

Additional recommendation.
If you have a WPF Solution (not UWP), then you should subscribe the command to CommandManager.RequerySuggested.
In this case, upon changes in the GUI, commands validation will be automatically called.
And the need to do this in the property setter will disappear.

    public class DelegateCommand : ICommand
    {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _execute;

        private readonly EventHandler requerySuggested;
        public event EventHandler CanExecuteChanged;

        public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
        {
            _execute = execute;
            _canExecute = canExecute;
            requerySuggested = (s, e) => RaiseCanExecuteChanged();
            CommandManager.RequerySuggested += requerySuggested;
        }

        public DelegateCommand(Action<object> execute) : this(execute, null) { }

        public virtual bool CanExecute(object parameter)
        {
            if (_canExecute == null)
            {
                return true;
            }

            return _canExecute(parameter);
        }

        public void Execute(object parameter)
        {
            _execute(parameter);
        }

        public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
        private string _company;
        public string Company { get => _company; set => SetProperty(ref _company, value); }

Also keep in mind that for a command bound to a WPF element in the GUI, the CanExecuteChanged event should always be called on the main thread of the application.
This also applies to calling it in the property setter, since the property can be changed not only from the GUI.
See also these implementations: BaseInpc, RelayCommand and RelayCommand<T> classes.

Upvotes: 4

lidqy
lidqy

Reputation: 2453

I think the problem is a very subtle nuance of the newer C# features for initializing get only properties. With the syntax you have:

public DelegateCommand  SubmitCommand => new DelegateCommand(Submit, CanSubmit);

You return a new Command instance every time your SumitCommand getter is invoked. The SubmitCommand instance you bind in XAML is a different instance as the one in Company setter: SubmitCommand.RaiseCanExecuteChanged(); The setter's SubmitCommand instance of course has no handler for CanExecuteChaned because its unknown to the UI. And no handler in code behind.

If you change that to:

public DelegateCommand  SubmitCommand { get; } = new DelegateCommand(Submit, CanSubmit);

I think the prob will be fixed. Because than you create the command just once and the buttons CanExecuteChanged handler will bind to the same SubmitCommand instance the company setter invokes RaiseCanExecuteChanged for.

Upvotes: 4

Related Questions