Asynchronous WPF Command
Asynchronous WPF command
The ICommand
interface provides a nice way of binding WPF buttons to methods in a view model. If those methods use any amount of time, though, you'll want to run them in a background thread.
This implementation of ICommand
is an easily-extendable way to run methods in an async manner when a button is clicked. The base class creates a BackgroundWorker
that runs a proxy for the Execute
method. All you have to do is override OnExecute
.
The...
BeforeExecute
...method allows you to run operations on the UI thread immediately before the BackgroundWorker
does its async operation.
The...
AfterExecute
...method allows you to run operations after the BackgroundWorker
completes its work. It also provides the Exception
object (if one was thrown) that was thrown in the background thread.
/// <summary> /// Implementation of <c>ICommand</c> that allows for asynchronous operation. /// </summary> public class AsyncCommandBase : ICommand { /// <summary> /// Raises the <c>CanExecuteChanged</c> event for the command. /// </summary> /// <remarks>This method should be invoked whenever the /// returned value of <c>CanExecute</c> changes.</remarks> protected void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs()); } /// <summary> /// When overridden in a derived class, performs operations in the UI thread /// before beginning the background operation. /// </summary> /// <param name="parameter">The parameter passed to the /// <c>Execute</c> method of the command.</param> protected virtual void BeforeExecute(object parameter) { } /// <summary> /// When overridden in a derived class, performs operations in a background /// thread when the <c>Execute</c> method is invoked. /// </summary> /// <param name="parameter">The parameter passed to the /// <c>Execute</c> method of the command.</param> protected virtual void OnExecute(object parameter) { } /// <summary> /// When overridden in a derived class, performs operations when the /// background execution has completed. /// </summary> /// <param name="parameter">The parameter passed to the /// <c>Execute</c> method of the command.</param> /// <param name="error">The error object that was thrown /// during the background operation, or null if no error was thrown.</param> protected virtual void AfterExecute(object parameter, Exception error) { } /// <summary> /// Occurs when changes occur that affect whether or not the command should execute. /// </summary> public event EventHandler CanExecuteChanged; /// <summary> /// When overridden in a derived class, defines the method that /// determines whether the command can execute in its /// current state. /// </summary> /// <param name="parameter"> /// Data used by the command. If the command does not require data to be passed, /// this object can be set to null. /// </param> /// <returns>True if this command can be executed; /// otherwise, false.</returns> public virtual bool CanExecute(object parameter) { return true; } /// <summary> /// Runs the command method in a background thread. /// </summary> /// <param name="parameter"> /// Data used by the command. If the command does not require data to be passed, /// this object can be set to null. /// </param> public void Execute(object parameter) { BeforeExecute(parameter); var bgw = new BackgroundWorker(); bgw.DoWork += (s, e) => { OnExecute(parameter); }; bgw.RunWorkerCompleted += (s, e) => { AfterExecute(parameter, e.Error); }; bgw.RunWorkerAsync(); } }