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:
1 comment:
Congratz...
You really did a good job here.
I got your example to make something alike
but in WCF tech.
Thanks.
Post a Comment