Well, this is an example to show how to create an Asynchronous methods / calls conforming to the Event-based Asynchronous pattern. This example demonstrate how to use BackgroundWorker and Asynchronous methods to report the progress status of the task you are performing.
File 1: Entity.cs
using System;
using System.ComponentModel;
using System.Threading;
using System.Runtime.Remoting.Messaging;
namespace BackgroundWorkerClient
{
internal class Entity
{
#region Private Declarations
private int _fileCount;
private int _folderCount;
private int _progress;
private ProgressStatus _status = ProgressStatus.Idle;
private bool _taskRunning;
private readonly object _sync = new object();
private AsyncContext _taskContext;
#endregion
#region Public Properties
public bool IsBusy { get { return _taskRunning; } }
#endregion
#region EventHandlers & Delegates
public event AsyncCompletedEventHandler OnTaskCompleted;
public event TaskProgressChangedEventHandler OnTaskProgressChanged;
private delegate void TaskWorkerDelegate(string path, AsyncOperation asyncOperation, AsyncContext asyncContext, out bool cancelled);
#endregion
#region Protected Virtual Methods
protected virtual void DoOnTaskCompleted(AsyncCompletedEventArgs e)
{
if (OnTaskCompleted != null)
OnTaskCompleted(this, e);
}
protected virtual void DoOnTaskProgressChanged(TaskProgressChangedEventArgs eventArgs)
{
if (OnTaskProgressChanged != null)
OnTaskProgressChanged(this, eventArgs);
}
#endregion
#region Asynchronous Operations
private void StartProcessing(string path, AsyncOperation asyncOperation, AsyncContext asyncContext, out bool cancelled)
{
cancelled = false;
_status = ProgressStatus.Started;
_fileCount = 0;
_folderCount = 0;
_progress = 0;
for (int folderCount = 0; folderCount < 100; folderCount++)
{
// if (_progress >= 100) _progress = 0;
// compute progress
// int _progress = 100 * (i + 1) / SelectedFiles.Length;
_progress = 100 * (folderCount + 1) / 100;
_status = ProgressStatus.ProcessingFolders;
_folderCount = folderCount;
path = string.Format("Project{0}", _folderCount);
asyncOperation.Post(e => DoOnTaskProgressChanged((TaskProgressChangedEventArgs)e), new TaskProgressChangedEventArgs(_status, path, _progress, null));
Thread.Sleep(100);
for (int fileCount = 0; fileCount < 100; fileCount++)
{
_status = ProgressStatus.ProcessingFiles;
_fileCount = fileCount;
path = string.Format("Project{0}, Class{1}.cs", _folderCount, _fileCount);
asyncOperation.Post(e => DoOnTaskProgressChanged((TaskProgressChangedEventArgs)e), new TaskProgressChangedEventArgs(_status, path, _progress, null));
if (asyncContext.IsCancelling)
{
cancelled = true;
_status = ProgressStatus.Stopped;
return;
}
Thread.Sleep(10);
}
if (asyncContext.IsCancelling)
{
cancelled = true;
_status = ProgressStatus.Stopped;
return;
}
}
_status = ProgressStatus.Completed;
}
private void TaskCompletedCallback(IAsyncResult asyncResult)
{
// Retrieve the delegate.
AsyncResult result = (AsyncResult)asyncResult;
// get the original worker delegate and the AsyncOperation instance
TaskWorkerDelegate worker = (TaskWorkerDelegate)result.AsyncDelegate;
AsyncOperation async = (AsyncOperation)asyncResult.AsyncState;
bool cancelled;
// finish the asynchronous operation
worker.EndInvoke(out cancelled, asyncResult);
// clear the running task flag
lock (_sync)
{
_taskRunning = false;
_taskContext = null;
}
// raise the completed event
AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null, cancelled, null);
async.PostOperationCompleted(e => DoOnTaskCompleted((AsyncCompletedEventArgs)e), completedArgs);
}
#endregion
#region Public Methods
public void StartAsync(string path)
{
TaskWorkerDelegate worker = StartProcessing;
AsyncCallback completedCallback = TaskCompletedCallback;
lock (_sync)
{
if (_taskRunning)
throw new InvalidOperationException("The control is currently busy.");
AsyncOperation async = AsyncOperationManager.CreateOperation(null);
AsyncContext asyncContext = new AsyncContext();
bool cancelled;
worker.BeginInvoke(path, async, asyncContext, out cancelled, completedCallback, async);
_taskRunning = true;
_taskContext = asyncContext;
}
}
public void CancelAsync()
{
lock (_sync)
{
if (_taskContext != null)
_taskContext.Cancel();
}
}
#endregion
}
internal class AsyncContext
{
private readonly object _sync = new object();
private bool _isCancelling;
public bool IsCancelling
{
get
{
lock (_sync) { return _isCancelling; }
}
}
public void Cancel()
{
lock (_sync) { _isCancelling = true; }
}
}
public enum ProgressStatus
{
Idle,
Started,
Stopped,
Completed,
ProcessingFiles,
ProcessingFolders
}
public class TaskProgressChangedEventArgs : ProgressChangedEventArgs
{
public string FileName { get; private set; }
public ProgressStatus Status { get; private set; }
public TaskProgressChangedEventArgs(ProgressStatus status, string fileName, int progressPercent, object userState)
: base(progressPercent, userState)
{
FileName = fileName;
Status = status;
}
}
public delegate void TaskProgressChangedEventHandler(object sender, TaskProgressChangedEventArgs e);
}
File 2: ProgressController.cs
using System.ComponentModel;
namespace BackgroundWorkerClient
{
public class ProgressController
{
private Entity _entity;
public event AsyncCompletedEventHandler OnCompleted;
public event TaskProgressChangedEventHandler OnProgressChanged;
public ProgressController()
{
Initialize();
}
private void Initialize()
{
_entity = new Entity();
_entity.OnTaskCompleted += DoOnCompleted;
_entity.OnTaskProgressChanged += DoOnProgressChanged;
}
public void Start(string path)
{
_entity.StartAsync(path);
}
public void Stop()
{
_entity.CancelAsync();
}
void DoOnProgressChanged(object sender, TaskProgressChangedEventArgs progressArgs)
{
if (OnProgressChanged != null)
OnProgressChanged(this, progressArgs);
}
void DoOnCompleted(object sender, AsyncCompletedEventArgs completedArgs)
{
if (OnCompleted != null)
{
OnCompleted(this, completedArgs);
}
}
}
}
File 3: AsyncClient.cs
using System;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;
namespace BackgroundWorkerClient
{
public class AsyncClient : Form
{
Label _lblStatus;
Button _btnStart;
Button _btnCancel;
ProgressBar _progressBar;
BackgroundWorker _backgroundWorker;
CheckBox _ckUseController;
ProgressController _controller;
public AsyncClient()
{
InitializeComponent();
InitializeController();
SetButtons(true);
}
private void InitializeController()
{
_controller = new ProgressController();
_controller.OnCompleted += OnControllerTaskCompleted;
_controller.OnProgressChanged += OnControllerProgressChanged;
}
#region Windows Form Designer generated code
void InitializeComponent()
{
this._lblStatus = new System.Windows.Forms.Label();
this._progressBar = new System.Windows.Forms.ProgressBar();
this._btnCancel = new System.Windows.Forms.Button();
this._btnStart = new System.Windows.Forms.Button();
this._backgroundWorker = new System.ComponentModel.BackgroundWorker();
this._ckUseController = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// _lblStatus
//
this._lblStatus.AutoSize = true;
this._lblStatus.Location = new System.Drawing.Point(12, 25);
this._lblStatus.Name = "_lblStatus";
this._lblStatus.Size = new System.Drawing.Size(101, 13);
this._lblStatus.TabIndex = 0;
this._lblStatus.Text = "Status: Not Started";
//
// _progressBar
//
this._progressBar.Location = new System.Drawing.Point(12, 43);
this._progressBar.MarqueeAnimationSpeed = 50;
this._progressBar.Name = "_progressBar";
this._progressBar.Size = new System.Drawing.Size(337, 21);
this._progressBar.Style = System.Windows.Forms.ProgressBarStyle.Continuous;
this._progressBar.TabIndex = 1;
//
// _btnCancel
//
this._btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this._btnCancel.Enabled = false;
this._btnCancel.Location = new System.Drawing.Point(274, 82);
this._btnCancel.Name = "_btnCancel";
this._btnCancel.Size = new System.Drawing.Size(75, 23);
this._btnCancel.TabIndex = 2;
this._btnCancel.Text = "&Cancel";
this._btnCancel.Click += new System.EventHandler(this.OnCancel);
//
// _btnStart
//
this._btnStart.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this._btnStart.Location = new System.Drawing.Point(183, 82);
this._btnStart.Name = "_btnStart";
this._btnStart.Size = new System.Drawing.Size(75, 23);
this._btnStart.TabIndex = 3;
this._btnStart.Text = "&Start";
this._btnStart.Click += new System.EventHandler(this.OnStart);
//
// _backgroundWorker
//
this._backgroundWorker.WorkerReportsProgress = true;
this._backgroundWorker.WorkerSupportsCancellation = true;
this._backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.OnDoWork);
this._backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.OnCompleted);
this._backgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.OnProgressChanged);
//
// _ckUseController
//
this._ckUseController.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this._ckUseController.AutoSize = true;
this._ckUseController.Location = new System.Drawing.Point(15, 86);
this._ckUseController.Name = "_ckUseController";
this._ckUseController.Size = new System.Drawing.Size(141, 17);
this._ckUseController.TabIndex = 4;
this._ckUseController.Text = "&Process using Controller";
this._ckUseController.UseVisualStyleBackColor = true;
//
// AsyncClient
//
this.ClientSize = new System.Drawing.Size(361, 117);
this.Controls.Add(this._ckUseController);
this.Controls.Add(this._lblStatus);
this.Controls.Add(this._btnStart);
this.Controls.Add(this._btnCancel);
this.Controls.Add(this._progressBar);
this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AsyncClient";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Async Client";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new AsyncClient());
}
void OnDoWork(object sender, DoWorkEventArgs doWorkArgs)
{
Debug.Assert(doWorkArgs.Argument != null);
BackgroundWorker backgroundWorker = sender as BackgroundWorker;
Debug.Assert(backgroundWorker != null);
int count = (int)doWorkArgs.Argument;
doWorkArgs.Result = null;
for (int progress = 0; progress <= count; progress += count / 10)
{
if (backgroundWorker.CancellationPending)
{
doWorkArgs.Cancel = true;
break;
}
Thread.Sleep(500);
backgroundWorker.ReportProgress(progress);
}
}
void OnProgressChanged(object sender, ProgressChangedEventArgs progressArgs)
{
_progressBar.Value = progressArgs.ProgressPercentage;
}
void OnCompleted(object sender, RunWorkerCompletedEventArgs completedEventArgs)
{
_lblStatus.Text = completedEventArgs.Cancelled ? "Process: Cancelled" : "Process: Completed";
SetButtons(true);
}
void OnControllerProgressChanged(object sender, TaskProgressChangedEventArgs progressArgs)
{
_lblStatus.Text = string.Format("{0} : {1}", progressArgs.Status, progressArgs.FileName);
_progressBar.Value = progressArgs.ProgressPercentage;
}
void OnControllerTaskCompleted(object sender, AsyncCompletedEventArgs completedArgs)
{
_lblStatus.Text = completedArgs.Cancelled ? "Process: Cancelled" : "Process: Completed";
SetButtons(true);
}
private void SetButtons(bool isCompleted)
{
_btnCancel.Enabled = !isCompleted;
_btnStart.Enabled = isCompleted;
_ckUseController.Enabled = isCompleted;
}
public void TraceThread()
{
Trace.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
void OnCancel(object sender, EventArgs e)
{
if (_ckUseController.Checked)
_controller.Stop();
else
_backgroundWorker.CancelAsync();
SetButtons(true);
}
void OnStart(object sender, EventArgs e)
{
if (_ckUseController.Checked)
_controller.Start("empty");
else
_backgroundWorker.RunWorkerAsync(100);
_lblStatus.Text = "Status: In Progress";
SetButtons(false);
}
}
}
Some of the useful links from MSDN:
