Legends
Legends

Reputation: 22712

Button stays disabled - DelegateCommand not re-evaluating CanExecute handler

Problem: Buttons never gets enabled.

<Button Name="btnCompareAxises"Command="{Binding CompareCommand}"
          Content="{Binding VM.CompareAxisButtonLabel}" 
        IsEnabled="{Binding VM.IsCompareButtonEnabled}">
</Button>

ViewModel constructor:

 this.CompareCommand = new DelegateCommand(CompareCommand, ValidateCompareCommand);

The problem seems to be related to the CanExecute eventhandler of the registered Command of the button. The CanExecute handler returns false when the application loads. This is fine, as the conditions are not met initially.

The canExecute handler only runs on application startup or when the button is clicked. You cannot click a disabled button, so the button stays disabled forever if the initial value returned form the CanExecute handler is false!

Question:
Do I have to enable the button again, only using the command bound to it. Something like, hey command please reevaluate if the conditions for this buttons are met ?

Why sits the IsEnabled property under section Coercion and not under local?

enter image description here

The command:

public class DelegateCommand : ICommand
{
    private readonly Func<object, bool> canExecute;
    private readonly Action<object> execute;

    public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        this.OnCanExecuteChanged();
    }

    protected virtual void OnCanExecuteChanged()
    {
        var handler = this.CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

Upvotes: 1

Views: 1112

Answers (1)

Legends
Legends

Reputation: 22712

Solved:

I had to adapt the DelegateCommand class to make it work:

I have added CommandManager.RequerySuggested to the public CanExecuteChanged Event property.

Now it will automatically re-evaluate the CanExecute method of the command when soemthing changes in the UI!

public class DelegateCommand : ICommand
{
    private readonly Func<object, bool> canExecute;
    private readonly Action<object> execute;

    public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        this.execute = execute;
        this.canExecute = canExecute;
    }

    /// CommandManager
    /// Go to the "References" part of your class library and select "Add Reference". 
    /// Look for an assembly called "PresentationCore" and add it.
    public event EventHandler CanExecuteChanged
    {
        add
        {
            _internalCanExecuteChanged += value;
          CommandManager.RequerySuggested += value;

        }
        remove
        {
            _internalCanExecuteChanged -= value;
            CommandManager.RequerySuggested -= value;
        }
    }

    event EventHandler _internalCanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return this.canExecute == null || this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        this.OnCanExecuteChanged();
    }

    protected virtual void OnCanExecuteChanged()
    {
        var handler = this._internalCanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

Removed this from the button:

 IsEnabled="{Binding VM.IsCompareButtonEnabled}"

The binding here is not necessary, as the CanExecute handler will take care of the enabled/disabled state of the button!

Upvotes: 2

Related Questions