I have a simple WPF program with an ICommand
. I am finding that the button doesn't enable/disable as I would expect. I can illustrate this best with a contrived code example:
class Reload : ICommand
{
private readonly BackgroundWorker _bworker = new BackgroundWorker();
public Reload()
{
this._isExecuting = false;
this._bworker.DoWork += this._bworker_DoWork;
this._bworker.RunWorkerCompleted += this._bworker_RunWorkerCompleted;
}
public event EventHandler CanExecuteChanged;
private void OnCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
this.CanExecuteChanged(this, EventArgs.Empty);
}
private bool _isExecuting;
private void SetIsExecuting(bool isExecuting)
{
this._isExecuting = isExecuting;
this.OnCanExecuteChanged();
}
public bool CanExecute(object parameter)
{
return !this._isExecuting;
}
public void Execute(object parameter)
{
//this does not update the GUI immediately
this.SetIsExecuting(true);
//This line doesn't fix my problem
CommandManager.InvalidateRequerySuggested();
//during this wait, button appears "clicked"
Thread.Sleep(TimeSpan.FromSeconds(2)); //simulate first calculation
this._bworker.RunWorkerAsync();
}
private void _bworker_DoWork(object sender, DoWorkEventArgs e)
{
//during this wait, button appears disabled
Thread.Sleep(TimeSpan.FromSeconds(2)); //simulate second calculation
}
private void _bworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//this updates immediately
this.SetIsExecuting(false);
}
}
In the Execute(object)
method, I trigger the CanExecuteChanged
event in a way that will cause CanExecute(object)
to return false. After that call, I expect the button to become disabled immediately, but it doesn't become disabled until some point between the call to RunWorkerAsync()
and the second simulated calculation.
In the background worker's RunWorkerCompleted(...)
event handler, I again trigger the CanExecuteChanged
event, but this time in a way that will cause CanExecuteChanged(object)
to return true. After this call, the button immediately becomes enabled.
Why does the button not immediately appear as disabled when I trigger the CanExecuteChanged
event?
Note #1: that the first simulated calculation represents code that I have that should run on the main GUI thread. If I remove this call, the button acts as I would expect.
Note #2: I've read about using CommandManager.InvalidateRequerySuggested()
to force the code to call the CanExecute(object)
method. I've shown in my comments that this isn't working for me. Considering that I call OnCanExecuteChanged(...)
, I think that that suggestion is redundant anyway.