In my application, I have a Command that I only want the user to be able to trigger if it is not already running. The command in question is bound to a WPF button which means that it automagically disables the button if CanExecute is false. So far so good.
Unfortunately, the operation performed by the command is a long running one, so it needs to occur on a different thread. I didn't think that would be a problem... but it seems it is.
I have extracted out a minimal sample that will show the problem. If bound to a button (via the LocalCommands.Problem static reference) the button will be disabled as desired. When the worker thread tries to update CanExecute, an InvalidOperationException will be thrown from inside System.Windows.Controls.Primitives.ButtonBase.
What is the most appropriate way to resolve this?
Sample command code below:
using System;
using System.Threading;
using System.Windows.Input;
namespace InvalidOperationDemo
{
static class LocalCommands
{
public static ProblemCommand Problem = new ProblemCommand();
}
class ProblemCommand : ICommand
{
private bool currentlyRunning = false;
private AutoResetEvent synchronize = new AutoResetEvent(false);
public bool CanExecute(object parameter)
{
return !CurrentlyRunning;
}
public void Execute(object parameter)
{
CurrentlyRunning = true;
ThreadPool.QueueUserWorkItem(ShowProblem);
}
private void ShowProblem(object state)
{
// Do some work here. When we're done, set CurrentlyRunning back to false.
// To simulate the problem, wait on the never-set synchronization object.
synchronize.WaitOne(500);
CurrentlyRunning = false;
}
public bool CurrentlyRunning
{
get { return currentlyRunning; }
private set
{
if (currentlyRunning == value) return;
currentlyRunning = value;
var onCanExecuteChanged = CanExecuteChanged;
if (onCanExecuteChanged != null)
{
try
{
onCanExecuteChanged(this, EventArgs.Empty);
}
catch (Exception e)
{
System.Windows.MessageBox.Show(e.Message, "Exception in event handling.");
}
}
}
}
public event EventHandler CanExecuteChanged;
}
}