-------------201504161039更新-------------
更新内容:
- IWaitForm接口删除System.Windows.Forms.DialogResult DialogResult属性。即隐藏等待窗体的方式不再分为设置DialogResult和调用Hide()两种,改为仅调用Hide()一种,简化设计。由于Hide()属于访问控件,执行器需根据自身是否会跨线程调用该方法而做出相应处理
- WaitUI增加私有方法HideWaitForm,用于隐藏等待窗体(由于会在后台线程调用该方法,故内部有跨线程处理),替代原来的设置DialogResult的做法
- WaitForm的FormClosing事件由注册该事件改为重写OnFormClosing方法,对阻止窗体关闭的条件增加了Visible,即当窗体处于可见时,才会阻止窗体关闭和触发UserCancelling事件,这是为了更准确的区分是执行器调用Hide()隐藏等待窗体,还是用户关闭等待窗体,仅通过e.CloseReason是不可靠的,因为当用户点过关闭按钮后,e.CloseReason就会是UserClosing,稍后执行器在调用Hide隐藏窗体时,仍然会进入OnFormClosing,此时e.CloseReason仍然是UserClosing,就会再次触发UserCancelling事件,虽然没什么影响,但实属不应该,加了Visible的话,执行器Hide窗体后,Visible就为false,就不会再次触发UserCancelling事件。当然,仍然建议自定义等待窗体屏蔽关闭按钮,让用户只能通过点击取消控件来取消任务,就没那么多事了。但不建议通过把ControlBox=false来整个隐藏右上角那仨按钮,因为我始终认为要给用户最小化的权利,我作为用户使用其它软件的时候,是很痛恨这种限制的
- 等待窗体的【取消】按钮单击后不再将Enabled置为false。原因是在基于BackgroundWorker的方案中,等待窗体Hide后有可能再次ShowDialog,也就是再次执行任务时依然要保证可取消
- 将WaitFormNullException异常的定义移至WaitForm.cs文件中(原先在WaitUI.cs中)。原因是等待窗体相关的东西应该与执行器保持独立
-------------20150415原文(已更新)-------------
适用环境:.net 2.0+的Winform项目。
先解释一下我所谓的【带等待窗体的任务执行器】是个什么鬼,就是可以用该类执行任意耗时方法(下文将把被执行的方法称为任务或任务方法),执行期间会显示一个模式等待窗体,让用户知道任务正在得到执行,程序并没有卡死。先看一下效果:
功能:
- 等待窗体可以使用执行器自带的默认窗体(就上图的样子),嫌丑你也可以使用自己精心设计的窗体,甚至基于Devexpress、C1等第三方漂亮窗体打造也是完全可以的
- 在任务中可以更新等待窗体上的Label、ProgressBar之类的控件以提供进度反馈。懒得反馈的话,就默认“请稍候...”+Marquee式滚动
- 如果任务允许被终止,用户可以通过某些操作终止任务执行(例如点击上图中的【取消】按钮);如果不允许,你可以把取消按钮隐藏了,或者在任务中不响应用户的终止请求就好
- 任务的执行结果(包括ref/out参数)、是否出现异常、是否被取消等情况都可以得到
原理:
- 调用任务所属委托的BeginInvoke,让任务在后台线程执行,随即在UI线程(通常就是主线程)调用等待窗体的ShowDialog弹出模式窗体,让用户知道任务正在执行的同时阻止用户进行其他操作。由于任务和等待窗体分别在不同的线程跑,所以等待窗体不会被卡住
- 任务执行期间可以通过执行器提供的一组属性和方法操作等待窗体上的控件,这组属性和方法内部是通过调用等待窗体的Invoke或BeginInovke对控件进行操作,实现跨线程访问控件
- 任务执行期间用户可以通过点击等待窗体上的【取消】按钮(如果你让它显示的话)或点击右上角关闭按钮发出终止任务的请求(等待窗体会拦截关闭操作),其结果是执行器的UserCancelling属性会置为true,所以在任务中可以访问该属性得知用户是否请求了取消操作,如果你同意终止的话,需设置执行器的Cancelled=true,并随即return出任务方法
- 任务执行完后(无论成功、异常、取消)会自动进入异步回调方法,回调方法中会首先访问Cancelled获知任务是否已取消,如果已取消,则直接return出回调方法;如果未取消,则调用任务所属委托的EndInvoke得到任务执行结果或异常。最后不管取消与否,finally块中会调用HideWaitForm(),以确保关闭等待窗体
- 等待窗体关闭后,执行器会继续执行ShowDialog后面的语句。如果任务已取消,则抛出特定异常报告调用者;如果任务存在异常,则抛出该异常;以上情况都不存在的话,返回任务结果
如述,功能简单,实现容易,我只是把这种需求通用化了一下,让还没有类似轮子的朋友可以拿去就用。另外还有个基于BackgroundWorker的实现方式,我可能会在下一篇文章分享。
先看一下大致的使用示例:
//WaitUI就是执行器 private void button1_Click(object sender, EventArgs es) { //可检测执行器是否正在执行另一个任务。其实基本不可能出现IsBusy=true,因为执行器工作时,用户做不了其它事 //老实说这个IsBusy要不要公开我还纠结了一下,因为公开了没什么用,但也没什么坏处,因为setter是private的 //Whatever~最后我还是选择公开,可能~因为爱情 //if (WaitUI.IsBusy) { return; } try { //WaitUI.RunXXX方法用于执行任务 //该方法的返回值就是任务的返回值 //任务抛出的异常会通过RunXXX方法抛出 //WaitUI.RunAction(Foo, 33, 66); //执行无返回值的方法 int r = WaitUI.RunFunc(Foo, 33, 66); //执行有返回值的方法 //object r = WaitUI.RunDelegate(new Func<int, int, int>(Foo), 33, 66);//执行委托 //WaitUI.RunAction(new MyWaitForm(), Foo);//指定自定义等待窗体执行任务,几个RunXXX方法都有可指定自定义窗体的重载 MessageBox.Show("任务完成。" + r); } catch (WorkCancelledException)//任务被取消是通过抛出该异常来报告 { MessageBox.Show("任务已取消!"); } catch (Exception ex)//任务抛出的异常 { MessageBox.Show("任务出现异常!" + ex.Message); } } //耗时任务。因为该方法会在后台线程执行,所以方法中不可以有访问控件的代码 int Foo(int a, int b) { //可以通过执行器的一系列公开属性和方法间接操作等待窗体的UI元素 WaitUI.CancelControlVisible = true;//设置取消任务的控件的可见性,即是否允许用户取消任务(默认是false:不可见) WaitUI.BarStyle = ProgressBarStyle.Continuous;//设置滚动条样式(默认是Marquee:循环梭动式) WaitUI.BarMaximum = 100; //设置滚动条值上限(默认是100) WaitUI.BarMinimum = 0; //设置滚动条值下限(默认是0) WaitUI.BarStep = 1; //设置滚动条步进幅度(默认是10) WaitUI.BarVisible = true; //设置滚动条是否可见(默认是true:可见) int i; for (i = a; i < b; i++) { if (WaitUI.UserCancelling)//响应用户的取消请求 { WaitUI.Cancelled = true;//告诉执行器任务已取消 return 0; } //可以抛个异常试试 //if (i == 43) { throw new NotSupportedException("异常测试"); } //可以随时再次操作等待窗体的各种UI元素 //if (i % 10 == 0) { WaitUI.CancelControlVisible = false; } //隐藏取消控件 //else if (i % 5 == 0) { WaitUI.CancelControlVisible = true; }//显示取消控件 WaitUI.WorkMessage = "正在XXOO,已完成 " + i + " 下..."; //更新进度描述 WaitUI.BarValue = i;//更新进度值 //WaitUI.BarPerformStep();//步进进度条 Thread.Sleep(50); } return i; }
看完示例,熟悉套路的你可能都已经能洞悉实现细节了,不过作为方案分享文章,我还是照章讲一下使用说明先,后面再掰扯设计说明,先看类图:
使用说明:
- WaitUI通过RunAction、RunFunc、RunDelegate这3个基本方法和它们的重载执行任务,看名字就知道,它们依次是执行无返回值方法、有返回值方法和自定义委托,每个方法都有不指定等待窗体和指定等待窗体两种重载形态,不指定时就使用方案自带的WaitForm作为等待窗体。自定义等待窗体需实现IWaitForm接口,详情在后面的设计说明部分有说。这里就表示等待窗体是在执行任务时才传进去的,任务执行完成后,WaitUI会销毁等待窗体,这是为了让WaitUI作为一个静态类,尽量短暂的持有对象,节约内存。所以如果传入的是自定义等待窗体的变量,请注意不要在WaitRun之后再次使用该变量,因为它已经被销毁,推荐的做法是直接在RunXXX中new一个自定义等待窗体。当然如果不嫌弃自带等待窗体,直接就不用传入,自然不会有这个问题。前两种方法是泛型方法,根据Action和Func这俩泛型委托重载,这俩委托支持到最多16个参数,但为了节约篇幅,方案中只重载了0~8个参数的情况,用户可以根据需要增加重载。它俩可以执行任意不多于8个参数的有返回或无返回方法,得益于编译器的智能推断,使用时非常方便,直接RunAction(Foo, arg1, arg2, ...)就好了,根本不用纠结到底要使用哪个重载。对于RunDelegate方法,接受的是一个委托实例,也就是不能直接传入方法,必须要用委托把方法套上才行。任何委托都可以传入,所以RunDelegate是最应万变的方法,当你的方法存在ref/out参数,或者参数个数变态到超过16个时,你还可以也只可以选用RunDelegate。但有个限制,委托有且只有绑定一个方法,RunXXX拒绝执行委托链
- RunFunc和RunDelegate方法有返回值,前者的返回类型与任务方法的返回类型一致,后者则是object。它俩的返回值就是任务方法的返回值。同样,任务抛出的异常一样会在3种RunXXX方法中抛出来,等于用RunXXX执行任务与直接调用任务相比,除了写法上有所不同:
int a = WaitUI.RunFunc(Foo,33); int b = Foo(33);
调用体验是一样一样的,所以你拿去就可以用,不需要对任务方法做什么修改,带个套就完事儿,除非任务方法存在下面这种情况
- 原理中说过,RunXXX方法实际上是调用任务所属委托的BeginInvoke方法,也就是异步执行任务,也就是任务会在另一个线程执行。所以任务中不能访问控件,这恐怕是该方案最大的不便,但确实原理所限,所以如果你的任务有访问控件的代码,还得做出改动才行。要问为什么非得让任务在后台,而等待窗体在前台,不可以调换过来吗?那样不就没这个不便了吗?那是因为等待窗体如果不在主线程ShowDialog,它就达不到模式的效果,用户仍然可以唱歌跳舞,这恐怕是你不愿意的
- 任务中可以通过WaitUI的一组属性和方法(WorkMessage、BarValue、BarPerformStep等)更新等待窗体中的文本呈现控件和进度指示控件(不限于Label和ProgressBar,取决于等待窗体的设计),用来向用户报告任务执行进度。当然不想做任何报告也可以,就让用户面对一个“请稍候...”和循环滚动条也无不可,具体文字和滚动条样式取决于等待窗体的默认设置
- WaitUI有个CancelControlVisible属性,可以设置为true/false控制等待窗体上是否显示【取消】按钮之类的控件(不限于Button,取决于等待窗体的设计,所以下文不说取消按钮,说取消控件)。这里需要详细说一下该方案的取消任务的机制,其实与BackgroundWorker的机制一致(好吧是我借鉴了它),熟悉bgw的老鸟请略过。显示取消控件只代表用户可以请求终止任务,至于你(或者说任务)是否响应这个请求(同意终止与否)是另一回事。什么意思,就是用户点击取消控件后,不是说任务就会自动终止了~凭什么会终止嘛对吧,任务在线程池,又不可能Abort,所以任务是否终止完全取决你在任务代码中的处理,比如你在任务中段就来个return或throw ex,这个就叫终止,任由代码执行下去,就叫做没终止,所以用户请求终止与任务是不是真的终止了没有必然联系。说这么多是什么意思,就是如果你要让用户看到取消控件,那么你就应该响应用户的请求,反过来如果不想任务被终止,那么就别让用户有发起请求的可能,当然这是与技术无关的纯人机交互理念的东西,没有对错,反正我是建议不要欺骗用户,下面说说如何响应终止请求。当用户发起终止请求后,WaitUI的UserCancelling会变为true,在任务中你可以根据这个值来做出终止任务的处理,但是在终止之前,还得麻烦你设置一个标记,千万别忘记,就是让WaitUI.Cancelled = true,这等于告诉执行器任务确实终止了,在设置完标记后,最好紧跟终止代码,不要再做其它事,让Cancelled与事实一致。执行器中根据Cancelled来获知任务是否已终止,进而做出相应的处理和返回。为什么不根据UserCancelling而是Cancelled相信你已经明白了,前者是用户的意愿,后者是开发者的决定,当然是决定靠谱。回到CancelControlVisible属性,这个属性建议在任务方法顶部就设置,因为一个任务是否可终止应该是确定的,通常来说,循环类任务是可以终止的,例如批量处理图片,一圈处理一张,那这种任务是可以也应该允许用户终止的;而非循环类任务,或者原子性比较强的任务,开始了就只能等待结果或报错,这种任务一方面可能就不允许用户终止,另一方面则是想终止都终止不了(比如WebRequest.GetResponse、SqlCommand.ExecuteNonQuery之类),这种任务最好就不提供取消控件给用户。不过CancelControlVisible也像WorkMessage之类的属性一样,是可以在任务中随时+反复设置的,所以你的任务可能有些阶段可被终止,有时则不允许终止,开开合合都是可以的,as you wish
- RunXXX有3种执行结果:①成功执行任务,返回任务返回值~如果任务有返回值的话;②任务产生异常,RunXXX会原样抛出该异常;③任务被终止,抛出WorkCancelledException异常(后面有关于为什么选择抛异常这种方式的说明)。你自行根据不同结果做相应处理
- 对于有ref/out参数的任务方法,如果你想在任务执行后取回,请注意要这样:
//正确 object[] prms = { a, b }; WaitUI.RunDelegate(new Action(Foo), prms); a = prms[0]; b = prms[1]; //错误 WaitUI.RunDelegate(new Action(Foo), a, b);
即要先构造好参数数组(哪怕只有1个参数),完了传入数组,最后从数组中取出的元素才是被蹂蹑过的。不能直接传入单个参数变量,那样你是不能通过该变量取回改动过的值的,具体原因自己悟
方案源码:
WaitUI.cs包含class WaitUI和2个异常类WorkIsBusyException、WorkCancelledException
using System; using System.Reflection; using System.Windows.Forms; namespace AhDung.WinForm { /// <summary> /// 执行任务并显示等候窗体 /// </summary> public static class WaitUI { static IWaitForm waitForm; //等待窗体 static object result; //任务返回结果 static Exception exception;//任务执行异常 static object[] parmsInput; //调用者传入的参数 static ParameterInfo[] parmsMethod;//任务所需的参数 static bool isCallBackCompleted; //指示回调方法是否已执行完毕 /// <summary> /// 指示用户是否已请求取消任务 /// </summary> public static bool UserCancelling { get; private set; } /// <summary> /// 指示任务是否已取消 /// </summary> public static bool Cancelled { private get; set; } /// <summary> /// 指示任务是否正在执行中 /// </summary> public static bool IsBusy { get; private set; } #region 一组操作等候窗体UI的属性/方法 /// <summary> /// 获取或设置进度描述 /// </summary> public static string WorkMessage { get { if (waitForm.InvokeRequired) { return waitForm.Invoke(new Func<string>(() => waitForm.WorkMessage)) as string; } return waitForm.WorkMessage; } set { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.WorkMessage = value)); return; } waitForm.WorkMessage = value; } } /// <summary> /// 获取或设置进度条可见性 /// </summary> public static bool BarVisible { get { if (waitForm.InvokeRequired) { return Convert.ToBoolean(waitForm.Invoke(new Func<bool>(() => waitForm.BarVisible))); } return waitForm.BarVisible; } set { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.BarVisible = value)); return; } waitForm.BarVisible = value; } } /// <summary> /// 获取或设置进度条动画样式 /// </summary> public static ProgressBarStyle BarStyle { get { if (waitForm.InvokeRequired) { return (ProgressBarStyle)(waitForm.Invoke(new Func<ProgressBarStyle>(() => waitForm.BarStyle))); } return waitForm.BarStyle; } set { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.BarStyle = value)); return; } waitForm.BarStyle = value; } } /// <summary> /// 获取或设置进度值 /// </summary> public static int BarValue { get { if (waitForm.InvokeRequired) { return Convert.ToInt32(waitForm.Invoke(new Func<int>(() => waitForm.BarValue))); } return waitForm.BarValue; } set { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.BarValue = value)); return; } waitForm.BarValue = value; } } /// <summary> /// 获取或设置进度条步进值 /// </summary> public static int BarStep { get { if (waitForm.InvokeRequired) { return Convert.ToInt32(waitForm.Invoke(new Func<int>(() => waitForm.BarStep))); } return waitForm.BarStep; } set { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.BarStep = value)); return; } waitForm.BarStep = value; } } /// <summary> /// 使进度条步进 /// </summary> public static void BarPerformStep() { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.BarPerformStep())); return; } waitForm.BarPerformStep(); } /// <summary> /// 获取或设置进度条上限值 /// </summary> public static int BarMaximum { get { if (waitForm.InvokeRequired) { return Convert.ToInt32(waitForm.Invoke(new Func<int>(() => waitForm.BarMaximum))); } return waitForm.BarMaximum; } set { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.BarMaximum = value)); return; } waitForm.BarMaximum = value; } } /// <summary> /// 获取或设置进度条下限值 /// </summary> public static int BarMinimum { get { if (waitForm.InvokeRequired) { return Convert.ToInt32(waitForm.Invoke(new Func<int>(() => waitForm.BarMinimum))); } return waitForm.BarMinimum; } set { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.BarMinimum = value)); return; } waitForm.BarMinimum = value; } } /// <summary> /// 获取或设置取消任务的控件的可见性 /// </summary> public static bool CancelControlVisible { get { if (waitForm.InvokeRequired) { return Convert.ToBoolean(waitForm.Invoke(new Func<bool>(() => waitForm.CancelControlVisible))); } return waitForm.CancelControlVisible; } set { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.CancelControlVisible = value)); return; } waitForm.CancelControlVisible = value; } } #endregion #region 公共方法:无返回值+默认窗体 /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction(Action method) { RunDelegate(method); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction<T>(Action<T> method, T arg) { RunDelegate(method, arg); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction<T1, T2>(Action<T1, T2> method, T1 arg1, T2 arg2) { RunDelegate(method, arg1, arg2); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction<T1, T2, T3>(Action<T1, T2, T3> method, T1 arg1, T2 arg2, T3 arg3) { RunDelegate(method, arg1, arg2, arg3); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4>(Action<T1, T2, T3, T4> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { RunDelegate(method, arg1, arg2, arg3, arg4); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { RunDelegate(method, arg1, arg2, arg3, arg4, arg5); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } #endregion #region 公共方法:无返回值+自定义窗体 /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction(IWaitForm fmWait, Action method) { RunDelegate(fmWait, method); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction<T>(IWaitForm fmWait, Action<T> method, T arg) { RunDelegate(fmWait, method, arg); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction<T1, T2>(IWaitForm fmWait, Action<T1, T2> method, T1 arg1, T2 arg2) { RunDelegate(fmWait, method, arg1, arg2); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction<T1, T2, T3>(IWaitForm fmWait, Action<T1, T2, T3> method, T1 arg1, T2 arg2, T3 arg3) { RunDelegate(fmWait, method, arg1, arg2, arg3); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4>(IWaitForm fmWait, Action<T1, T2, T3, T4> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { RunDelegate(fmWait, method, arg1, arg2, arg3, arg4); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4, T5>(IWaitForm fmWait, Action<T1, T2, T3, T4, T5> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4, T5, T6>(IWaitForm fmWait, Action<T1, T2, T3, T4, T5, T6> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4, T5, T6, T7>(IWaitForm fmWait, Action<T1, T2, T3, T4, T5, T6, T7> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static void RunAction<T1, T2, T3, T4, T5, T6, T7, T8>(IWaitForm fmWait, Action<T1, T2, T3, T4, T5, T6, T7, T8> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } #endregion #region 公共方法:有返回值+默认窗体 /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<TResult>(Func<TResult> method) { return (TResult)RunDelegate(method); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<T, TResult>(Func<T, TResult> method, T arg) { return (TResult)RunDelegate(method, arg); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<T1, T2, TResult>(Func<T1, T2, TResult> method, T1 arg1, T2 arg2) { return (TResult)RunDelegate(method, arg1, arg2); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> method, T1 arg1, T2 arg2, T3 arg3) { return (TResult)RunDelegate(method, arg1, arg2, arg3); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, T5, T6, TResult>(Func<T1, T2, T3, T4, T5, T6, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, T5, T6, T7, TResult>(Func<T1, T2, T3, T4, T5, T6, T7, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } /// <summary> /// 执行方法并显示默认等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } #endregion #region 公共方法:有返回值+自定义窗体 /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<TResult>(IWaitForm fmWait, Func<TResult> method) { return (TResult)RunDelegate(fmWait, method); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<T, TResult>(IWaitForm fmWait, Func<T, TResult> method, T arg) { return (TResult)RunDelegate(fmWait, method, arg); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<T1, T2, TResult>(IWaitForm fmWait, Func<T1, T2, TResult> method, T1 arg1, T2 arg2) { return (TResult)RunDelegate(fmWait, method, arg1, arg2); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, TResult>(IWaitForm fmWait, Func<T1, T2, T3, TResult> method, T1 arg1, T2 arg2, T3 arg3) { return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, T5, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, T5, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, T5, T6, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, T5, T6, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, T5, T6, T7, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, T5, T6, T7, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) { return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); } /// <summary> /// 执行方法并显示自定义等候窗体 /// </summary> public static TResult RunFunc<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(IWaitForm fmWait, Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) { return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); } #endregion /// <summary> /// 执行委托并显示默认等候窗体 /// </summary> public static object RunDelegate(Delegate del, params object[] args) { return RunDelegate(new WaitForm(), del, args); } /// <summary> /// 执行委托并显示自定义等候窗体 /// </summary> public static object RunDelegate(IWaitForm fmWait, Delegate del, params object[] args) { if (IsBusy) { throw new WorkIsBusyException(); } if (fmWait == null) { throw new WaitFormNullException(); } if (del == null || del.GetInvocationList().Length != 1) { throw new ApplicationException("委托不能为空,且只能绑定1个方法!"); } if (args == null) { throw new ArgumentNullException("args"); } MethodInfo beginInvoke = del.GetType().GetMethod("BeginInvoke"); object[] parmsBeginInvoke = new object[beginInvoke.GetParameters().Length]; if (args.Length > parmsBeginInvoke.Length - 2) { throw new ArgumentException("提供的参数超过了方法所需的参数!"); } parmsMethod = del.Method.GetParameters();//假定GetParameters总是返回按参数Position排序的数组,如果将来有问题,要查验这个假设 parmsInput = args; try { //赋值BeginInvoke参数 parmsInput.CopyTo(parmsBeginInvoke, 0); //塞入传入的参数 for (int i = parmsInput.Length; i < parmsMethod.Length; i++) //对未传入的参数赋予默认值 { ParameterInfo p = parmsMethod[i]; object pVal; if ((pVal = p.DefaultValue) == DBNull.Value) //若参数不具有默认值则抛异常 { throw new ArgumentException(string.Format("方法所需的参数{0}没有定义默认值,必须传入!", p.Name)); } parmsBeginInvoke[i] = pVal; } parmsBeginInvoke[parmsBeginInvoke.Length - 2] = new AsyncCallback(Callback);//倒数第2个参数 parmsBeginInvoke[parmsBeginInvoke.Length - 1] = del; //倒数第1个参数 //重置状态 IsBusy = true; Cancelled = false; exception = null; isCallBackCompleted = false; waitForm = fmWait; fmWait.UserCancelling += WaitForm_UserCancelling;//注册用户取消任务事件 beginInvoke.Invoke(del, parmsBeginInvoke); if (!isCallBackCompleted)//这里要判断一下,极端情况下有可能还没等ShowDialog,回调就执行完了 { fmWait.ShowDialog(); //务必确保ShowDialog不会抛异常 } //返回 if (Cancelled) { throw new WorkCancelledException(); } if (exception != null) { throw exception; } return result; } finally { Release(); UserCancelling = false; IsBusy = false; } } /// <summary> /// 回调方法 /// </summary> private static void Callback(IAsyncResult ar) { try { if (Cancelled) { return; } //若任务取消就不必EndInvoke了 MethodInfo endInvoke = ar.AsyncState.GetType().GetMethod("EndInvoke"); object[] parmsEndInvoke = new object[endInvoke.GetParameters().Length]; if (parmsEndInvoke.Length != 1)//若方法存在ref或out参数,赋值给endInvoke参数 { int i = 0; foreach (ParameterInfo p in parmsMethod) { if (p.ParameterType.IsByRef) { parmsEndInvoke[i++] = parmsInput[p.Position]; } } } parmsEndInvoke[parmsEndInvoke.Length - 1] = ar; result = endInvoke.Invoke(ar.AsyncState, parmsEndInvoke); if (parmsEndInvoke.Length != 1)//从endInvoke参数取出值返给输入参数 { int i = 0; foreach (ParameterInfo p in parmsMethod) { if (p.ParameterType.IsByRef) { parmsInput[p.Position] = parmsEndInvoke[i++]; } } } } catch (TargetInvocationException ex) { exception = ex.InnerException; } catch (Exception ex) { exception = ex; } finally { HideWaitForm(); isCallBackCompleted = true; } } /// <summary> /// 隐藏等待窗体 /// </summary> /// <remarks>因为该方法会在回调中调用,所以要做跨线程处理</remarks> static void HideWaitForm() { if (waitForm == null) { return; } if (waitForm.InvokeRequired) { waitForm.BeginInvoke(new Action(() => waitForm.Hide())); return; } waitForm.Hide(); } /// <summary> /// 用户请求取消任务时 /// </summary> private static void WaitForm_UserCancelling(object sender, EventArgs e) { UserCancelling = true; } /// <summary> /// 释放资源 /// </summary> private static void Release() { parmsInput = null;//这里不会影响调用者传入的object[]实例,因为不是ref进来的 parmsMethod = null; IDisposable disp; if ((disp = waitForm as IDisposable) != null) { disp.Dispose(); } } } /// <summary> /// 任务正在执行 /// </summary> public class WorkIsBusyException : InvalidOperationException { public WorkIsBusyException() : base("任务正在执行!") { } } /// <summary> /// 任务已被取消 /// </summary> public class WorkCancelledException : ApplicationException { public WorkCancelledException() : base("任务已被取消!") { } } }
WaitForm.cs包含interface IWaitForm、class WaitForm和异常类WaitFormNullException,其中WaitForm为了屏蔽关闭按钮,使用了WinFormHelper.cs
using System; using System.Windows.Forms; namespace AhDung.WinForm { /// <summary> /// 等待窗体 /// </summary> ///<remarks>IWaitForm的默认实现</remarks> public class WaitForm : Form, IWaitForm { /// <summary> /// Required designer variable. /// </summary> private readonly System.ComponentModel.IContainer components = null; /// <summary> /// Clean up any resources being used. /// </summary> /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.lbMsg = new System.Windows.Forms.Label(); this.bar = new System.Windows.Forms.ProgressBar(); this.btnCancel = new System.Windows.Forms.Button(); this.SuspendLayout(); // // lbMsg // this.lbMsg.Location = new System.Drawing.Point(10, 20); this.lbMsg.Name = "lbMsg"; this.lbMsg.Size = new System.Drawing.Size(386, 55); this.lbMsg.TabIndex = 0; this.lbMsg.Text = "正在处理,请稍候..."; // // bar // this.bar.Location = new System.Drawing.Point(12, 78); this.bar.Name = "bar"; this.bar.Step = 1; this.bar.Size = new System.Drawing.Size(384, 16); this.bar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; this.bar.TabIndex = 1; // // btnCancel // this.btnCancel.Location = new System.Drawing.Point(321, 109); this.btnCancel.Name = "btnCancel"; this.btnCancel.Size = new System.Drawing.Size(75, 23); this.btnCancel.TabIndex = 2; this.btnCancel.Text = "取消"; this.btnCancel.UseVisualStyleBackColor = true; this.btnCancel.Visible = false; // // FmWaitForDesign // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(408, 155); this.Controls.Add(this.btnCancel); this.Controls.Add(this.bar); this.Controls.Add(this.lbMsg); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; this.MaximizeBox = false; this.Name = "FmWaitForDesign"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "请稍候..."; this.ResumeLayout(false); } #endregion System.Windows.Forms.Label lbMsg; System.Windows.Forms.Button btnCancel; System.Windows.Forms.ProgressBar bar; public WaitForm() { InitializeComponent(); btnCancel.Click += btnCancel_Click;//注册取消按钮单击事件 } #region 将【取消】按钮点击、窗体关闭等行为视为触发【取消任务】事件 protected override void OnFormClosing(FormClosingEventArgs e) { //阻止用户关闭窗体并触发UserCancelling事件 //加Visible是因为调用Hide()也会触发该事件,为了避免再次OnUserCancelling为之 if (e.CloseReason == CloseReason.UserClosing && this.Visible) { e.Cancel = true; this.OnUserCancelling(); } base.OnFormClosing(e); } private void btnCancel_Click(object sender, EventArgs e) { this.OnUserCancelling(); } protected virtual void OnUserCancelling() { if (UserCancelling != null) { UserCancelling(this, EventArgs.Empty); } } //屏蔽窗体关闭按钮 protected override void OnVisibleChanged(EventArgs e) { base.OnVisibleChanged(e); if (this.Visible) { AhDung.WinForm.WinFormHelper.DisableCloseButton(this); } } #endregion #region 实现接口 public string WorkMessage { get { return lbMsg.Text; } set { lbMsg.Text = value; } } public bool BarVisible { get { return bar.Visible; } set { bar.Visible = value; } } public ProgressBarStyle BarStyle { get { return bar.Style; } set { bar.Style = value; } } public int BarValue { get { return bar.Value; } set { bar.Value = value; } } public int BarStep { get { return bar.Step; } set { bar.Step = value; } } public void BarPerformStep() { bar.PerformStep(); } public bool CancelControlVisible { get { return btnCancel.Visible; } set { btnCancel.Visible = value; } } public int BarMaximum { get { return bar.Maximum; } set { bar.Maximum = value; } } public int BarMinimum { get { return bar.Minimum; } set { bar.Minimum = value; } } public event EventHandler UserCancelling; #endregion } /// <summary> /// 等待窗体规范 /// </summary> public interface IWaitForm { #region 用于操作等待窗体UI表现的属性和方法,实现时不用操心线程问题,让客户端(任务执行器)去操心 /// <summary> /// 获取或设置进度描述 /// </summary> /// <remarks>建议默认值为“请稍候...”之类的字眼</remarks> string WorkMessage { get; set; } /// <summary> /// 获取或设置进度条的可见性 /// </summary> /// <remarks>建议默认值为true</remarks> bool BarVisible { get; set; } /// <summary> /// 获取或设置进度条的动画样式 /// </summary> /// <remarks>建议默认值为Marquee</remarks> ProgressBarStyle BarStyle { get; set; } /// <summary> /// 获取或设置进度条的值 /// </summary> /// <remarks>建议默认值为0</remarks> int BarValue { get; set; } /// <summary> /// 获取或设置进度条的步进幅度 /// </summary> int BarStep { get; set; } /// <summary> /// 使进度条步进 /// </summary> void BarPerformStep(); /// <summary> /// 获取或设置取消任务的控件的可见性 /// </summary> /// <remarks>建议默认值为false</remarks> bool CancelControlVisible { get; set; } /// <summary> /// 获取或设置进度条的值上限 /// </summary> /// <remarks>建议默认值为100</remarks> int BarMaximum { get; set; } /// <summary> /// 获取或设置进度条的值下限 /// </summary> /// <remarks>建议默认值为0</remarks> int BarMinimum { get; set; } #endregion /// <summary> /// 显示模式等待窗体 /// </summary> /// <remarks>建议使用Form类的默认实现</remarks> DialogResult ShowDialog(); #region Invoke相关,供客户端在跨线程操作窗体UI /// <summary> /// 指示是否需要使用Invoke操作窗体控件 /// </summary> /// <remarks>建议使用Form类的默认实现</remarks> bool InvokeRequired { get; } /// <summary> /// 窗体Invoke方法 /// </summary> /// <remarks>建议使用Form类的默认实现</remarks> object Invoke(Delegate method); /// <summary> /// 窗体BeginInvoke方法 /// </summary> /// <remarks>建议使用Form类的默认实现</remarks> IAsyncResult BeginInvoke(Delegate method); #endregion /// <summary> /// 隐藏等待窗体 /// </summary> /// <remarks>建议使用Form类的默认实现</remarks> void Hide(); /// <summary> /// 当用户请求取消任务时 /// </summary> /// <remarks>应在用户交互取消控件、关闭窗体时触发该事件</remarks> event EventHandler UserCancelling; } /// <summary> /// 等候窗体为空 /// </summary> public class WaitFormNullException : ApplicationException { public WaitFormNullException() : base("等待窗体不能为null!") { } } }
using System; using System.Runtime.InteropServices; using System.Windows.Forms; namespace AhDung.WinForm { public static class WinFormHelper { [DllImport("User32.dll ")] private static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert); [DllImport("User32.dll ")] private static extern int RemoveMenu(IntPtr hMenu, int nPosition, int wFlags); const int MF_REMOVE = 0x1000; //const int SC_RESTORE = 0xF120; //还原 //const int SC_MOVE = 0xF010; //移动 //const int SC_SIZE = 0xF000; //大小 //const int SC_MINIMIZE = 0xF020; //最小化 //const int SC_MAXIMIZE = 0xF030; //最大化 const int SC_CLOSE = 0xF060; //关闭 /// <summary> /// 屏蔽窗体关闭功能 /// </summary> public static void DisableCloseButton(IWin32Window form) { IntPtr hMenu = GetSystemMenu(form.Handle, false); RemoveMenu(hMenu, SC_CLOSE, MF_REMOVE); } } }
可怜的.net 2.0~3.5没有足够的内置委托,所以你可能还需要Delegates.cs
namespace System { //无返回委托 public delegate void Action(); //public delegate void Action<in T>(T arg);//这个2.0是有的 public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2); public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3); public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); public delegate void Action<in T1, in T2, in T3, in T4, in T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); //public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); //public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); //public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); //public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); //public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); //public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); //public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); //public delegate void Action<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); //有返回委托 public delegate TResult Func<out TResult>(); public delegate TResult Func<in T, out TResult>(T arg); public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2); public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3); public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6); public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7); public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8); //public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9); //public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10); //public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11); //public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12); //public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13); //public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14); //public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15); //public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, in T6, in T7, in T8, in T9, in T10, in T11, in T12, in T13, in T14, in T15, in T16, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, T10 arg10, T11 arg11, T12 arg12, T13 arg13, T14 arg14, T15 arg15, T16 arg16); }
--------------华丽丽的分隔线--------------
下面的内容献给闲的蛋疼的童鞋,又或者你想鄙视、教育我这里不该怎样怎样,那里应该怎样怎样的话,请看完再继续。
设计说明:
- 为什么要把WaitUI弄成静态类。new一个执行器执行任务,完了销毁执行器不是很自然的事吗,弄成静态类就不怕资源占用、状态管理不好造成BUG吗。的确我有考虑过弄成实例类,但思考之后还是决定静态之,原因有二:①使用简单。这是从考虑做这个东西之初到实施过程中都始终优先考虑的原则,new一下是费不了多少事,但new完以后是不是得设置某些属性,得Run,得释放,像这样:
using(WaitUI w = new WaitUI()) { w.CancelControlVisible = true; w.RunAction(Foo); }
怎么都不如一句WaitUI.RunAction(Foo)来的简单;②不必改造任务方法。想象一下,实例类的话,任务中想更新等待窗体,是不是得获得执行器实例的引用,或是某个包装了执行器实例的类的实例,怎么获得,自然是通过任务方法的参数传进去,像这样:
int Foo(int a, int b, WaitUI w) { w.WorkMessage = ""; ...
其结果就是必然要改造任务方法,而我的目的一是让用户拿去就能用,结果还要改这改那的我都闲害臊;二是让任务方法既可以套上执行器执行,也可以不带套照常执行,所以WorkMessage那些属性都是写成绿色无公害的,不管有没有等待窗体,都不会抛异常,目的就是即便任务方法中增加了这些语句,也可以在不带套的情况下照常执行
- 为什么要弄个IWaitForm这样的胖子接口。的确这个地方我承认弄成基类比较合适,除了不至于弄出个胖接口之外,更重要的是可以做一些基础实现,不至于什么都交给自定义等待窗体编写者去实现,省事都是其次,关键是能做一些必要的控制,比如UserCancelling事件,要求用户在点击取消按钮和关闭窗体时触发,但编写者只在其中一种操作时触发或根本不触发那也没办法,一个道理,过分灵活不是好事。而为什么我仍然选择接口,也恰恰是因为要保证灵活,就是要允许编写者从其它第三方Form继承,设计美观的等待窗体,如果设计为基类,那就堵死了这种可能,等于我在灵活性和健壮性之间选择了前者。当然编写者可以把WaitFormBase的基类改为目标第三方Form,还有Label、ProgressBar、Button都改为三方控件,反正源码都在,爱怎么玩怎么玩
- WaitForm重写了Form.OnVisibleChanged方法,是为了屏蔽右上角关闭按钮,不屏蔽也是可以的,但必须在FormClosing事件中阻止窗体关闭,同时触发UserCancelling事件,我两样的做了,也建议自定义等待窗体编写者做足全套,因为不屏蔽关闭按钮的话,用户点了却关不掉,感觉怪怪的。另外说说为什么UserCancelling要弄成事件,而不是弄成一个bool属性,当用户取消时置该属性为true,完了让WaitUI.UserCancelling直接访问该属性,为什么?原因是这个IWaitForm,我不希望它专供WaitUI使用,其它执行器或类似方案也可以用,那其他方案请求取消任务的操作未必是通过给UserCancelling类似的属性做标记,人家有可能是执行一个方法,比如BackgroundWorker就是调用CancelAsync()(虽然它内部也是设置标记),那IWaitForm如果是设置某个属性,就等于要执行器主动来获取标记,而不是等待窗体主动通知执行器,显然对BackgroundWorker这样的就不好使了,总不能单独弄个timer循环获取标记。弄成事件就灵活多了,等待窗体只负责在适当的时候触发这个事件,至于执行器如何响应这个事件,自行处理,WaitUI可以设置UserCancelling,而BackgroundWorker可以调用CancelAsync(),各找各妈。另外,WaitForm作为IWaitForm的默认实现,它可以作为自定义等待窗体的实现参考
- 任务取消为什么要采用抛出异常的方式,为什么不是让调用者判断Cancelled属性就好。这里我也纠结了一下,最后决定选择前者的理由是,RunXXX是要负责返回任务结果的,如果任务被取消,那么RunXXX的返回值就是不可靠的,如果任由调用者接到这个不可靠的值,这是对调用者不负责的,所以必须抛出异常,阻止调用者获得不可靠的返回值。BackgroundWorker之所以采用Cancelled方式,是因为它已经通过另一种方式阻止调用者获得错误结果,就是当e.Cancelled为true时,访问e.Result会抛异常,等于理念是一样的,效果也是一样的
- 关于WaitUI.IsBusy,这货请参看使用示例中的注释,作为公开成员可有可无
最后真心希望路过大虾对方案不妥之处指点一二,在此谢过。
-文毕-