一.引子
耗时操作时,我们常常会提供用户一个等待界面,来提醒用户操作还在进行中,并没有结束。方法往往有2种,一种就是同步处理,在操作完成前要一直等待,这样有造成系统假死的可能;另外一种方法就是异步处理,我下面说到的方法就是采用异步处理,并集成到组件中,暴露出启动,取消,完成3个事件给用户,增加友好度。可以在处理过程中取消操作,也可以先去处理其他的任务,等到操作的完成。
二.效果
三.主要思路
public virtual void BeginAsyncOperation(object taskId)
{
//根据任务号创建跟踪异步操作的生存期对象
AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(taskId);
//多线程将访问任务字典,所以要锁定任务字典
lock (_userTaskList.SyncRoot)
{
if (_userTaskList.Contains(taskId))//如果已经包含了该任务,则抛出异常
{
throw new ArgumentException("参数任务号ID必须唯一!","参数出错");
}
_taskId = taskId;
//将任务ID与跟踪异步操作对象关联!!!
_userTaskList[taskId] = asyncOp;
}
//开始真正的异步操作
WorkerEventHandler workerDelegate = new WorkerEventHandler(OnCalculateWorker);
workerDelegate.BeginInvoke(asyncOp, null, null);
}
上面是启动的代码,从中我们不难看出asyncOp被作为了参数在OnCalculateWorker方法中来具体实现异步的操作。
而OnCalculateWorker的代码如下:
protected virtual void OnCalculateWorker(AsyncOperation asyncOp)
{
if (!TaskCanceled(asyncOp.UserSuppliedState))
{
Exception e = null;
try
{
if (CalculateWorker != null)
{
WorkerProgressEventArgs e1 = new WorkerProgressEventArgs(asyncOp, 0);
CalculateWorker(this,e1);
}
}
catch (Exception ex)
{
e = ex;
}
//处理结束后的操作
this.CompletionMethod(e,TaskCanceled(asyncOp.UserSuppliedState),asyncOp);
}
}
其中TaskCanceled是判断是否被取消的方法,CalculateWorker是被暴露的耗时操作的事件。我们只要在需要处理复杂操作时建立一个该组件的实例,并在CalculateWorker事件中添加复杂处理的代码就可以完成复杂操作。CompletionMethod方法处理在结束操作时的一些处理。下面时它的代码:
private void CompletionMethod(Exception exception, bool canceled, AsyncOperation asyncOp)
{
//如果没有取消,任务将在结束后依据标识从集合中移除当前的异步操作对象
if (!canceled)
{
//先锁定任务字典,然后从任务字典中移除该任务
lock (_userTaskList.SyncRoot)
{
_userTaskList.Remove(asyncOp.UserSuppliedState);
}
//在任务结束后,跟踪异步对象要结束生存期
//结束异步调用的生存期(触发OnCompletedDelegate),这个时候会发生回调操作
asyncOp.PostOperationCompleted(onCompletedDelegate, new AsyncCompletedEventArgs(null, false, null));
}
else
{
//在任务结束后,跟踪异步对象要结束生存期
//结束异步调用的生存期(触发OnCompletedDelegate),这个时候会发生回调操作
asyncOp.PostOperationCompleted(onCompletedDelegate,null);
}
}
其中,onCompletedDelegate是一个委托,它的关联方法是
private void CalculateCompleted(object operationState)
{
AsyncCompletedEventArgs e = operationState as AsyncCompletedEventArgs;
OnCalculateCompleted(e);
}
其中,OnCalculateCompleted的代码如下:
protected virtual void OnCalculateCompleted(AsyncCompletedEventArgs e)
{
if (e != null)
{
if (AsyncEventCompleted != null && !e.Cancelled)
AsyncEventCompleted(this, e);
}
if (_isCanceled)
_isCanceled = false;
}
从上可见,引发了AsyncEventCompleted事件。该事件也正是我前面提到的异步完成的事件。
下面是调用该组件的代码:
this.Text = "begin!";
Guid guid = Guid.NewGuid();
_guid = guid;
this.jcsAsyncComponet1.BeginAsyncOperation(guid);
this._frm = new FrmWait();
_frm.Owner = this;
_frm.StartPosition = FormStartPosition.CenterScreen;
_frm.CloseEvent += new EventHandler(_frm_CloseEvent);
_frm.Show();
其中_frm就是我们在前面看到的图片对应的窗体。该窗体中用到了我前面提到的等待控件JcsWaitControl,这个不重要,我们放一个ProcessBar也是可以的。
下面是取消按钮的操作,以及_guid变量的定义:
void _frm_CloseEvent(object sender, EventArgs e)
{
button1.PerformClick();
}
private Guid _guid;
private void button1_Click(object sender, EventArgs e)
{
if(_guid !=null)
this.jcsAsyncComponet1.CancelAsync(_guid);
this.Text = "Canceled!";
if (_frm != null)
_frm.Close();
}
四.总结说明
上面的代码虽然不是很完整,但已经基本上反映出来该组件的实现方法。因为只是范例,没有做更好的优化,在一些细节处理上或许还存在疏漏。感兴趣的朋友,也可以自己动手试上一试。