Klaus Nji
Klaus Nji

Reputation: 18857

WPF Command bindings not updating button enabled state

Simple RelayCommand defined as follows:

  public class RelayCommand : IDelegateCommand
    {
        readonly Predicate<object> _canExecute;
        readonly Action<object> _execute;

        public RelayCommand(Predicate<object> canExecute, Action<object> execute)
            : this(canExecute, execute, true)
        {
        }

        public RelayCommand(Predicate<object> canExecute, Action<object> execute, bool isCommandAllowed)
        {
            _canExecute = canExecute;
            _execute = execute;
            IsAllowed = isCommandAllowed;
        }

        public void RaiseCanExecuteChanged()
        {
            CanExecuteChanged(this, EventArgs.Empty);
        }

        #region ICommand Members

        public virtual bool CanExecute(object parameter)
        {
            return _canExecute(parameter);
        }

        public event EventHandler CanExecuteChanged = delegate { };

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

IDelegateCommand is defined as follows:

public interface IDelegateCommand : ICommand
{
    void RaiseCanExecuteChanged();
}

Is bound to a WPF Button as follows:

<Button Command="{Binding StartSimulationCommand}" Content="Start Simulation"/>

And corresponding ViewModel usage of the Command is given as follows:

   public class MainViewModel 
    {
          // missing irrelevant bits

          public ICommand StartSimulationCommand
                {
                    get { return new RelayCommand(arg => true, arg =>
                    {
                        var inputValidationResponse = ValidateInputs();
                        if (!string.IsNullOrEmpty(inputValidationResponse))
                        {
                            _logger.Error(inputValidationResponse);
                            return;
                        }

                        // this method opens a websocket and if that operation is 
                        // successful, property called IsWebSocketOpen is updated.
                        OpenWebSocketChannel();

                        // update command states
                        StopSimulationCommand.RaiseCanExecuteChanged();

                    });
                    }
                }


            public RelayCommand StopSimulationCommand
            {
                get { return new RelayCommand(arg => IsWebSocketOpened, arg => { CloseWebSocketChannel(); }); }
            }

                  private bool _isWebSocketOpened;
            public bool IsWebSocketOpened {
                get { return _isWebSocketOpened; }
                set { SetField(ref _isWebSocketOpened, value, "IsWebSocketOpened"); }
            }
}

When I invoke StopSimulationCommand.RaiseCanExecuteChanged, the state of the button bound to StopSimulationCommand does not changed to enabled, even through the predicate for that command now returns true.

What did I miss?

Upvotes: 0

Views: 982

Answers (1)

mm8
mm8

Reputation: 169200

You are creating a new instance of the RelayCommand in the setter of the StopSimulationCommand. You should create the command once in the constructor of the view model class:

public class MainViewModel : INotifyPropertyChanged
{
    public MainViewModel ()
    {
        StopSimulationCommand = new RelayCommand(arg => IsWebSocketOpened, arg => { CloseWebSocketChannel(); });
    }

    public RelayCommand StopSimulationCommand { get; private set; }
}

Upvotes: 2

Related Questions