【C#】分享带等待窗体的任务执行器一枚

开发 后端 开发工具
先解释一下我所谓的【带等待窗体的任务执行器】是个什么鬼,就是可以用该类执行任意耗时方法(下文将把被执行的方法称为任务或任务方法),执行期间会显示一个模式等待窗体,让用户知道任务正在得到执行,程序并没有卡死。先看一下效果:

适用环境:.net 2.0+的Winform项目。

先解释一下我所谓的【带等待窗体的任务执行器】是个什么鬼,就是可以用该类执行任意耗时方法(下文将把被执行的方法称为任务或任务方法),执行期间会显示一个模式等待窗体,让用户知道任务正在得到执行,程序并没有卡死。先看一下效果:

【C#】分享带等待窗体的任务执行器一枚

功能:

等待窗体可以使用执行器自带的默认窗体(就上图的样子),嫌丑你也可以使用自己精心设计的窗体,甚至基于Devexpress、C1等第三方漂亮窗体打造也是完全可以的
在任务中可以更新等待窗体上的Label、ProgressBar之类的控件以提供进度反馈。懒得反馈的话,就默认“请稍候...”+Marquee式滚动
如果任务允许被终止,用户可以通过某些操作终止任务执行(例如点击上图中的【取消】按钮);如果不允许,你可以把取消按钮隐藏了,或者在任务中不响应用户的终止请求就好
任务的执行结果(包括ref/out参数)、是否出现异常、是否被取消等情况都可以得到

原理:

调用任务所属委托的BeginInvoke,让任务在后台线程执行,随即在UI线程(通常就是主线程)调用等待窗体的ShowDialog弹出模式窗体,让用户知道任务正在执行的同时阻止用户进行其他操作。由于任务和等待窗体分别在不同的线程跑,所以等待窗体不会被卡住

任务执行期间可以通过执行器提供的一组属性和方法操作等待窗体上的控件,这组属性和方法内部是通过调用等待窗体的Invoke或BeginInovke对控件进行操作,实现跨线程访问控件
任务执行期间用户可以通过点击等待窗体上的【取消】按钮(如果你让它显示的话)或点击右上角关闭按钮发出终止任务的请求(等待窗体会拦截关闭操 作),其结果是执行器的UserCancelling属性会置为true,所以在任务中可以访问该属性得知用户是否请求了取消操作,如果你同意终止的话, 需设置执行器的Cancelled=true,并随即return出任务方法

任务执行完后(无论成功、异常、取消)会自动进入异步回调方法,回调方法中会首先访问Cancelled获知任务是否已取消,如果已取消,则直接 return出回调方法;如果未取消,则调用任务所属委托的EndInvoke得到任务执行结果或异常。最后不管取消与否,finally块中会设置等待 窗体的DialogResult属性,以确保关闭等待窗体

等待窗体关闭后,执行器会继续执行ShowDialog后面的语句。如果任务已取消,则抛出特定异常报告调用者;如果任务存在异常,则抛出该异常;以上情况都不存在的话,返回任务结果

如述,功能简单,实现容易,我只是把这种需求通用化了一下,让还没有类似轮子的朋友可以拿去就用。另外还有个基于BackgroundWorker的实现方式,我可能会在下一篇文章分享。

先看一下大致的使用示例:

 

  1. //WaitUI就是执行器 
  2. private void button1_Click(object sender, EventArgs es) 
  3.     //可检测执行器是否正在执行另一个任务。其实基本不可能出现IsBusy=true,因为执行器工作时,用户做不了其它事 
  4.     //老实说这个IsBusy要不要公开我还纠结了一下,因为公开了没什么用,但也没什么坏处,因为setter是private的 
  5.     //Whatever~最后我还是选择公开,可能~因为爱情 
  6.     //if (WaitUI.IsBusy) { return; } 
  7.  
  8.     try 
  9.     { 
  10.         //WaitUI.RunXXX方法用于执行任务 
  11.         //该方法的返回值就是任务的返回值 
  12.         //任务抛出的异常会通过RunXXX方法抛出 
  13.  
  14.         //WaitUI.RunAction(Foo, 33, 66);                                      //执行无返回值的方法 
  15.         int r = WaitUI.RunFunc(Foo, 3366);                                  //执行有返回值的方法 
  16.         //object r = WaitUI.RunDelegate(new Func<int, int, int>(Foo), 33, 66);//执行委托 
  17.         //WaitUI.RunAction(new MyWaitForm(), Foo);//指定自定义等待窗体执行任务,几个RunXXX方法都有可指定自定义窗体的重载 
  18.  
  19.         MessageBoxEx.Show("任务完成。" + r); 
  20.     } 
  21.     catch (WorkCancelledException)//任务被取消是通过抛出该异常来报告 
  22.     { 
  23.         MessageBoxEx.Show("任务已取消!"); 
  24.     } 
  25.     catch (Exception ex)//任务抛出的异常 
  26.     { 
  27.         MessageBoxEx.Show("任务出现异常!" + ex.Message); 
  28.     } 
  29.  
  30. //耗时任务。因为该方法会在后台线程执行,所以方法中不可以有访问控件的代码 
  31. int Foo(int a, int b) 
  32.     //可以通过执行器的一系列公开属性和方法间接操作等待窗体的UI元素 
  33.     WaitUI.CancelControlVisible = true;//设置取消任务的控件的可见性,即是否允许用户取消任务(默认是false:不可见) 
  34.     WaitUI.BarStyle = ProgressBarStyle.Continuous;//设置滚动条样式(默认是Marquee:循环梭动式) 
  35.     WaitUI.BarMaximum = 100;      //设置滚动条值上限(默认是100) 
  36.     WaitUI.BarMinimum = 0;        //设置滚动条值下限(默认是0) 
  37.     WaitUI.BarStep = 1;           //设置滚动条步进幅度(默认是10) 
  38.     WaitUI.BarVisible = true;     //设置滚动条是否可见(默认是true:可见) 
  39.  
  40.     int i; 
  41.     for (i = a; i < b; i++) 
  42.     { 
  43.         if (WaitUI.UserCancelling)//响应用户的取消请求 
  44.         { 
  45.             WaitUI.Cancelled = true;//告诉执行器任务已取消 
  46.             return 0
  47.         } 
  48.  
  49.         //可以抛个异常试试 
  50.         //if (i == 43) { throw new NotSupportedException("异常测试"); } 
  51.  
  52.         //可以随时再次操作等待窗体的各种UI元素 
  53.         //if (i % 10 == 0) { WaitUI.CancelControlVisible = false; }   //隐藏取消控件 
  54.         //else if (i % 5 == 0) { WaitUI.CancelControlVisible = true; }//显示取消控件 
  55.         WaitUI.WorkMessage = "正在XXOO,已完成 " + i + " 下..."//更新进度描述 
  56.         WaitUI.BarValue = i;//更新进度值 
  57.         //WaitUI.BarPerformStep();//步进进度条 
  58.  
  59.         Thread.Sleep(100); 
  60.     } 
  61.     return i; 

看完示例,熟悉套路的你可能都已经能洞悉实现细节了,不过作为方案分享文章,我还是照章讲一下使用说明先,后面再掰扯设计说明,先看类图:

【C#】分享带等待窗体的任务执行器一枚

使用说明:

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执行任务与直接调用任务相比,除了写法上有所不同:

 

  1. int a = WaitUI.RunFunc(Foo,33); 
  2. 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参数的任务方法,如果你想在任务执行后取回,请注意要这样:

  1. //正确 
  2. object[] prms = { a, b }; 
  3. WaitUI.RunDelegate(new Action(Foo), prms); 
  4. a = prms[0]; 
  5. b = prms[1]; 
  6.  
  7. //错误 
  8. WaitUI.RunDelegate(new Action(Foo), a, b); 

 

即要先构造好参数数组(哪怕只有1个参数),完了传入数组,最后从数组中取出的元素才是被蹂蹑过的。不能直接传入单个参数变量,那样你是不能通过该变量取回改动过的值的,具体原因自己悟

方案源码:

WaitUI.cs包含class WaitUI和3个异常类WaitFormNullException、WorkIsBusyException、WorkCancelledException

  1. using System; 
  2. using System.Reflection; 
  3. using System.Windows.Forms; 
  4.  
  5. namespace AhDung.WinForm 
  6.     /// <summary> 
  7.     /// 执行任务并显示等候窗体 
  8.     /// </summary> 
  9.     public static class WaitUI 
  10.     { 
  11.         static IWaitForm waitForm; //等待窗体 
  12.         static object result;      //任务返回结果 
  13.         static Exception exception;//任务执行异常 
  14.  
  15.         static object[] parmsInput;        //调用者传入的参数 
  16.         static ParameterInfo[] parmsMethod;//任务所需的参数 
  17.         static bool isCallBackCompleted;   //指示回调方法是否已执行完毕 
  18.  
  19.         /// <summary> 
  20.         /// 指示用户是否已请求取消任务 
  21.         /// </summary> 
  22.         public static bool UserCancelling 
  23.         { 
  24.             get; 
  25.             private set; 
  26.         } 
  27.  
  28.         /// <summary> 
  29.         /// 指示任务是否已取消 
  30.         /// </summary> 
  31.         public static bool Cancelled 
  32.         { 
  33.             private get; 
  34.             set; 
  35.         } 
  36.  
  37.         /// <summary> 
  38.         /// 指示任务是否正在执行中 
  39.         /// </summary> 
  40.         public static bool IsBusy 
  41.         { 
  42.             get; 
  43.             private set; 
  44.         } 
  45.  
  46.         #region 一系列操作等候窗体UI的属性/方法 
  47.  
  48.         /// <summary> 
  49.         /// 获取或设置进度描述 
  50.         /// </summary> 
  51.         public static string WorkMessage 
  52.         { 
  53.             get 
  54.             { 
  55.                 if (waitForm.InvokeRequired) 
  56.                 { 
  57.                     return waitForm.Invoke(new Func<object>(() => waitForm.WorkMessage)) as string; 
  58.                 } 
  59.                 return waitForm.WorkMessage; 
  60.             } 
  61.             set 
  62.             { 
  63.                 if (waitForm == null) { return; } 
  64.  
  65.                 if (waitForm.InvokeRequired) 
  66.                 { 
  67.                     waitForm.BeginInvoke(new Action(() => waitForm.WorkMessage = value)); 
  68.                     return
  69.                 } 
  70.                 waitForm.WorkMessage = value; 
  71.             } 
  72.         } 
  73.  
  74.         /// <summary> 
  75.         /// 获取或设置进度条可见性 
  76.         /// </summary> 
  77.         public static bool BarVisible 
  78.         { 
  79.             get 
  80.             { 
  81.                 if (waitForm.InvokeRequired) 
  82.                 { 
  83.                     return Convert.ToBoolean(waitForm.Invoke(new Func<object>(() => waitForm.BarVisible))); 
  84.                 } 
  85.                 return waitForm.BarVisible; 
  86.             } 
  87.             set 
  88.             { 
  89.                 if (waitForm == null) { return; } 
  90.  
  91.                 if (waitForm.InvokeRequired) 
  92.                 { 
  93.                     waitForm.BeginInvoke(new Action(() => waitForm.BarVisible = value)); 
  94.                     return
  95.                 } 
  96.                 waitForm.BarVisible = value; 
  97.             } 
  98.         } 
  99.  
  100.         /// <summary> 
  101.         /// 获取或设置进度条动画样式 
  102.         /// </summary> 
  103.         public static ProgressBarStyle BarStyle 
  104.         { 
  105.             get 
  106.             { 
  107.                 if (waitForm.InvokeRequired) 
  108.                 { 
  109.                     return (ProgressBarStyle)(waitForm.Invoke(new Func<object>(() => waitForm.BarStyle))); 
  110.                 } 
  111.                 return waitForm.BarStyle; 
  112.             } 
  113.             set 
  114.             { 
  115.                 if (waitForm == null) { return; } 
  116.  
  117.                 if (waitForm.InvokeRequired) 
  118.                 { 
  119.                     waitForm.BeginInvoke(new Action(() => waitForm.BarStyle = value)); 
  120.                     return
  121.                 } 
  122.                 waitForm.BarStyle = value; 
  123.             } 
  124.         } 
  125.  
  126.         /// <summary> 
  127.         /// 获取或设置进度值 
  128.         /// </summary> 
  129.         public static int BarValue 
  130.         { 
  131.             get 
  132.             { 
  133.                 if (waitForm.InvokeRequired) 
  134.                 { 
  135.                     return Convert.ToInt32(waitForm.Invoke(new Func<object>(() => waitForm.BarValue))); 
  136.                 } 
  137.                 return waitForm.BarValue; 
  138.             } 
  139.             set 
  140.             { 
  141.                 if (waitForm == null) { return; } 
  142.  
  143.                 if (waitForm.InvokeRequired) 
  144.                 { 
  145.                     waitForm.BeginInvoke(new Action(() => waitForm.BarValue = value)); 
  146.                     return
  147.                 } 
  148.                 waitForm.BarValue = value; 
  149.             } 
  150.         } 
  151.  
  152.         /// <summary> 
  153.         /// 获取或设置进度条步进值 
  154.         /// </summary> 
  155.         public static int BarStep 
  156.         { 
  157.             get 
  158.             { 
  159.                 if (waitForm.InvokeRequired) 
  160.                 { 
  161.                     return Convert.ToInt32(waitForm.Invoke(new Func<object>(() => waitForm.BarStep))); 
  162.                 } 
  163.                 return waitForm.BarStep; 
  164.             } 
  165.             set 
  166.             { 
  167.                 if (waitForm == null) { return; } 
  168.  
  169.                 if (waitForm.InvokeRequired) 
  170.                 { 
  171.                     waitForm.BeginInvoke(new Action(() => waitForm.BarStep = value)); 
  172.                     return
  173.                 } 
  174.                 waitForm.BarStep = value; 
  175.             } 
  176.         } 
  177.  
  178.         /// <summary> 
  179.         /// 使进度条步进 
  180.         /// </summary> 
  181.         public static void BarPerformStep() 
  182.         { 
  183.             if (waitForm == null) { return; } 
  184.  
  185.             if (waitForm.InvokeRequired) 
  186.             { 
  187.                 waitForm.BeginInvoke(new Action(() => waitForm.BarPerformStep())); 
  188.                 return
  189.             } 
  190.             waitForm.BarPerformStep(); 
  191.         } 
  192.  
  193.         /// <summary> 
  194.         /// 获取或设置进度条上限值 
  195.         /// </summary> 
  196.         public static int BarMaximum 
  197.         { 
  198.             get 
  199.             { 
  200.                 if (waitForm.InvokeRequired) 
  201.                 { 
  202.                     return Convert.ToInt32(waitForm.Invoke(new Func<object>(() => waitForm.BarMaximum))); 
  203.                 } 
  204.                 return waitForm.BarMaximum; 
  205.             } 
  206.             set 
  207.             { 
  208.                 if (waitForm == null) { return; } 
  209.  
  210.                 if (waitForm.InvokeRequired) 
  211.                 { 
  212.                     waitForm.BeginInvoke(new Action(() => waitForm.BarMaximum = value)); 
  213.                     return
  214.                 } 
  215.                 waitForm.BarMaximum = value; 
  216.             } 
  217.         } 
  218.  
  219.         /// <summary> 
  220.         /// 获取或设置进度条下限值 
  221.         /// </summary> 
  222.         public static int BarMinimum 
  223.         { 
  224.             get 
  225.             { 
  226.                 if (waitForm.InvokeRequired) 
  227.                 { 
  228.                     return Convert.ToInt32(waitForm.Invoke(new Func<object>(() => waitForm.BarMinimum))); 
  229.                 } 
  230.                 return waitForm.BarMinimum; 
  231.             } 
  232.             set 
  233.             { 
  234.                 if (waitForm == null) { return; } 
  235.  
  236.                 if (waitForm.InvokeRequired) 
  237.                 { 
  238.                     waitForm.BeginInvoke(new Action(() => waitForm.BarMinimum = value)); 
  239.                     return
  240.                 } 
  241.                 waitForm.BarMinimum = value; 
  242.             } 
  243.         } 
  244.  
  245.         /// <summary> 
  246.         /// 获取或设置取消任务的控件的可见性 
  247.         /// </summary> 
  248.         public static bool CancelControlVisible 
  249.         { 
  250.             get 
  251.             { 
  252.                 if (waitForm.InvokeRequired) 
  253.                 { 
  254.                     return Convert.ToBoolean(waitForm.Invoke(new Func<object>(() => waitForm.CancelControlVisible))); 
  255.                 } 
  256.                 return waitForm.CancelControlVisible; 
  257.             } 
  258.             set 
  259.             { 
  260.                 if (waitForm == null) { return; } 
  261.  
  262.                 if (waitForm.InvokeRequired) 
  263.                 { 
  264.                     waitForm.BeginInvoke(new Action(() => waitForm.CancelControlVisible = value)); 
  265.                     return
  266.                 } 
  267.                 waitForm.CancelControlVisible = value; 
  268.             } 
  269.         } 
  270.  
  271.         #endregion 
  272.  
  273.         #region 公共方法:无返回值+默认窗体 
  274.  
  275.         /// <summary> 
  276.         /// 执行方法并显示默认等候窗体 
  277.         /// </summary> 
  278.         public static void RunAction(Action method) 
  279.         { 
  280.             RunDelegate(method); 
  281.         } 
  282.  
  283.         /// <summary> 
  284.         /// 执行方法并显示默认等候窗体 
  285.         /// </summary> 
  286.         public static void RunAction<T>(Action<T> method, T arg) 
  287.         { 
  288.             RunDelegate(method, arg); 
  289.         } 
  290.  
  291.         /// <summary> 
  292.         /// 执行方法并显示默认等候窗体 
  293.         /// </summary> 
  294.         public static void RunAction<T1, T2>(Action<T1, T2> method, T1 arg1, T2 arg2) 
  295.         { 
  296.             RunDelegate(method, arg1, arg2); 
  297.         } 
  298.  
  299.         /// <summary> 
  300.         /// 执行方法并显示默认等候窗体 
  301.         /// </summary> 
  302.         public static void RunAction<T1, T2, T3>(Action<T1, T2, T3> method, T1 arg1, T2 arg2, T3 arg3) 
  303.         { 
  304.             RunDelegate(method, arg1, arg2, arg3); 
  305.         } 
  306.  
  307.         /// <summary> 
  308.         /// 执行方法并显示默认等候窗体 
  309.         /// </summary> 
  310.         public static void RunAction<T1, T2, T3, T4>(Action<T1, T2, T3, T4> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4) 
  311.         { 
  312.             RunDelegate(method, arg1, arg2, arg3, arg4); 
  313.         } 
  314.  
  315.         /// <summary> 
  316.         /// 执行方法并显示默认等候窗体 
  317.         /// </summary> 
  318.         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) 
  319.         { 
  320.             RunDelegate(method, arg1, arg2, arg3, arg4, arg5); 
  321.         } 
  322.  
  323.         /// <summary> 
  324.         /// 执行方法并显示默认等候窗体 
  325.         /// </summary> 
  326.         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) 
  327.         { 
  328.             RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6); 
  329.         } 
  330.  
  331.         /// <summary> 
  332.         /// 执行方法并显示默认等候窗体 
  333.         /// </summary> 
  334.         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) 
  335.         { 
  336.             RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 
  337.         } 
  338.  
  339.         /// <summary> 
  340.         /// 执行方法并显示默认等候窗体 
  341.         /// </summary> 
  342.         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) 
  343.         { 
  344.             RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 
  345.         } 
  346.  
  347.         #endregion 
  348.  
  349.         #region 公共方法:无返回值+自定义窗体 
  350.  
  351.         /// <summary> 
  352.         /// 执行方法并显示自定义等候窗体 
  353.         /// </summary> 
  354.         public static void RunAction(IWaitForm fmWait, Action method) 
  355.         { 
  356.             RunDelegate(fmWait, method); 
  357.         } 
  358.  
  359.         /// <summary> 
  360.         /// 执行方法并显示自定义等候窗体 
  361.         /// </summary> 
  362.         public static void RunAction<T>(IWaitForm fmWait, Action<T> method, T arg) 
  363.         { 
  364.             RunDelegate(fmWait, method, arg); 
  365.         } 
  366.  
  367.         /// <summary> 
  368.         /// 执行方法并显示自定义等候窗体 
  369.         /// </summary> 
  370.         public static void RunAction<T1, T2>(IWaitForm fmWait, Action<T1, T2> method, T1 arg1, T2 arg2) 
  371.         { 
  372.             RunDelegate(fmWait, method, arg1, arg2); 
  373.         } 
  374.  
  375.         /// <summary> 
  376.         /// 执行方法并显示自定义等候窗体 
  377.         /// </summary> 
  378.         public static void RunAction<T1, T2, T3>(IWaitForm fmWait, Action<T1, T2, T3> method, T1 arg1, T2 arg2, T3 arg3) 
  379.         { 
  380.             RunDelegate(fmWait, method, arg1, arg2, arg3); 
  381.         } 
  382.  
  383.         /// <summary> 
  384.         /// 执行方法并显示自定义等候窗体 
  385.         /// </summary> 
  386.         public static void RunAction<T1, T2, T3, T4>(IWaitForm fmWait, Action<T1, T2, T3, T4> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4) 
  387.         { 
  388.             RunDelegate(fmWait, method, arg1, arg2, arg3, arg4); 
  389.         } 
  390.  
  391.         /// <summary> 
  392.         /// 执行方法并显示自定义等候窗体 
  393.         /// </summary> 
  394.         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) 
  395.         { 
  396.             RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5); 
  397.         } 
  398.  
  399.         /// <summary> 
  400.         /// 执行方法并显示自定义等候窗体 
  401.         /// </summary> 
  402.         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) 
  403.         { 
  404.             RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6); 
  405.         } 
  406.  
  407.         /// <summary> 
  408.         /// 执行方法并显示自定义等候窗体 
  409.         /// </summary> 
  410.         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) 
  411.         { 
  412.             RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 
  413.         } 
  414.  
  415.         /// <summary> 
  416.         /// 执行方法并显示自定义等候窗体 
  417.         /// </summary> 
  418.         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) 
  419.         { 
  420.             RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 
  421.         } 
  422.  
  423.         #endregion 
  424.  
  425.         #region 公共方法:有返回值+默认窗体 
  426.  
  427.         /// <summary> 
  428.         /// 执行方法并显示默认等候窗体 
  429.         /// </summary> 
  430.         public static TResult RunFunc<TResult>(Func<TResult> method) 
  431.         { 
  432.             return (TResult)RunDelegate(method); 
  433.         } 
  434.  
  435.         /// <summary> 
  436.         /// 执行方法并显示默认等候窗体 
  437.         /// </summary> 
  438.         public static TResult RunFunc<T, TResult>(Func<T, TResult> method, T arg) 
  439.         { 
  440.             return (TResult)RunDelegate(method, arg); 
  441.         } 
  442.  
  443.         /// <summary> 
  444.         /// 执行方法并显示默认等候窗体 
  445.         /// </summary> 
  446.         public static TResult RunFunc<T1, T2, TResult>(Func<T1, T2, TResult> method, T1 arg1, T2 arg2) 
  447.         { 
  448.             return (TResult)RunDelegate(method, arg1, arg2); 
  449.         } 
  450.  
  451.         /// <summary> 
  452.         /// 执行方法并显示默认等候窗体 
  453.         /// </summary> 
  454.         public static TResult RunFunc<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> method, T1 arg1, T2 arg2, T3 arg3) 
  455.         { 
  456.             return (TResult)RunDelegate(method, arg1, arg2, arg3); 
  457.         } 
  458.  
  459.         /// <summary> 
  460.         /// 执行方法并显示默认等候窗体 
  461.         /// </summary> 
  462.         public static TResult RunFunc<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> method, T1 arg1, T2 arg2, T3 arg3, T4 arg4) 
  463.         { 
  464.             return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4); 
  465.         } 
  466.  
  467.         /// <summary> 
  468.         /// 执行方法并显示默认等候窗体 
  469.         /// </summary> 
  470.         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) 
  471.         { 
  472.             return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5); 
  473.         } 
  474.  
  475.         /// <summary> 
  476.         /// 执行方法并显示默认等候窗体 
  477.         /// </summary> 
  478.         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) 
  479.         { 
  480.             return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6); 
  481.         } 
  482.  
  483.         /// <summary> 
  484.         /// 执行方法并显示默认等候窗体 
  485.         /// </summary> 
  486.         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) 
  487.         { 
  488.             return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 
  489.         } 
  490.  
  491.         /// <summary> 
  492.         /// 执行方法并显示默认等候窗体 
  493.         /// </summary> 
  494.         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) 
  495.         { 
  496.             return (TResult)RunDelegate(method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 
  497.         } 
  498.  
  499.         #endregion 
  500.  
  501.         #region 公共方法:有返回值+自定义窗体 
  502.  
  503.         /// <summary> 
  504.         /// 执行方法并显示自定义等候窗体 
  505.         /// </summary> 
  506.         public static TResult RunFunc<TResult>(IWaitForm fmWait, Func<TResult> method) 
  507.         { 
  508.             return (TResult)RunDelegate(fmWait, method); 
  509.         } 
  510.  
  511.         /// <summary> 
  512.         /// 执行方法并显示自定义等候窗体 
  513.         /// </summary> 
  514.         public static TResult RunFunc<T, TResult>(IWaitForm fmWait, Func<T, TResult> method, T arg) 
  515.         { 
  516.             return (TResult)RunDelegate(fmWait, method, arg); 
  517.         } 
  518.  
  519.         /// <summary> 
  520.         /// 执行方法并显示自定义等候窗体 
  521.         /// </summary> 
  522.         public static TResult RunFunc<T1, T2, TResult>(IWaitForm fmWait, Func<T1, T2, TResult> method, T1 arg1, T2 arg2) 
  523.         { 
  524.             return (TResult)RunDelegate(fmWait, method, arg1, arg2); 
  525.         } 
  526.  
  527.         /// <summary> 
  528.         /// 执行方法并显示自定义等候窗体 
  529.         /// </summary> 
  530.         public static TResult RunFunc<T1, T2, T3, TResult>(IWaitForm fmWait, Func<T1, T2, T3, TResult> method, T1 arg1, T2 arg2, T3 arg3) 
  531.         { 
  532.             return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3); 
  533.         } 
  534.  
  535.         /// <summary> 
  536.         /// 执行方法并显示自定义等候窗体 
  537.         /// </summary> 
  538.         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) 
  539.         { 
  540.             return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4); 
  541.         } 
  542.  
  543.         /// <summary> 
  544.         /// 执行方法并显示自定义等候窗体 
  545.         /// </summary> 
  546.         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) 
  547.         { 
  548.             return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5); 
  549.         } 
  550.  
  551.         /// <summary> 
  552.         /// 执行方法并显示自定义等候窗体 
  553.         /// </summary> 
  554.         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) 
  555.         { 
  556.             return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6); 
  557.         } 
  558.  
  559.         /// <summary> 
  560.         /// 执行方法并显示自定义等候窗体 
  561.         /// </summary> 
  562.         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) 
  563.         { 
  564.             return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7); 
  565.         } 
  566.  
  567.         /// <summary> 
  568.         /// 执行方法并显示自定义等候窗体 
  569.         /// </summary> 
  570.         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) 
  571.         { 
  572.             return (TResult)RunDelegate(fmWait, method, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 
  573.         } 
  574.  
  575.         #endregion 
  576.  
  577.         /// <summary> 
  578.         /// 执行委托并显示默认等候窗体 
  579.         /// </summary> 
  580.         public static object RunDelegate(Delegate del, params object[] args) 
  581.         { 
  582.             return RunDelegate(new WaitForm(), del, args); 
  583.         } 
  584.  
  585.         /// <summary> 
  586.         /// 执行委托并显示自定义等候窗体 
  587.         /// </summary> 
  588.         public static object RunDelegate(IWaitForm fmWait, Delegate del, params object[] args) 
  589.         { 
  590.             if (IsBusy) { throw new WorkIsBusyException(); } 
  591.  
  592.             if (fmWait == null) { throw new WaitFormNullException(); } 
  593.             if (del == null || del.GetInvocationList().Length != 1) { throw new ApplicationException("委托不能为空,且只能绑定1个方法!"); } 
  594.             if (args == null) { throw new ArgumentNullException("args"); } 
  595.  
  596.             MethodInfo beginInvoke = del.GetType().GetMethod("BeginInvoke"); 
  597.             object[] parmsBeginInvoke = new object[beginInvoke.GetParameters().Length]; 
  598.             if (args.Length > parmsBeginInvoke.Length - 2
  599.             { 
  600.                 throw new ArgumentException("提供的参数超过了方法所需的参数!"); 
  601.             } 
  602.  
  603.             parmsMethod = del.Method.GetParameters();//假定GetParameters总是返回按参数Position排序的数组,如果将来有问题,要查验这个假设 
  604.             parmsInput = args; 
  605.  
  606.             try 
  607.             { 
  608.                 //赋值BeginInvoke参数 
  609.                 parmsInput.CopyTo(parmsBeginInvoke, 0); //塞入传入的参数 
  610.                 for (int i = parmsInput.Length; i < parmsMethod.Length; i++) //对未传入的参数赋予默认值 
  611.                 { 
  612.                     ParameterInfo p = parmsMethod[i]; 
  613.                     object pVal; 
  614.  
  615.                     if ((pVal = p.DefaultValue) == DBNull.Value) //若参数不具有默认值则抛异常 
  616.                     { throw new ArgumentException(string.Format("方法所需的参数{0}没有定义默认值,必须传入!", p.Name)); } 
  617.  
  618.                     parmsBeginInvoke[i] = pVal; 
  619.                 } 
  620.                 parmsBeginInvoke[parmsBeginInvoke.Length - 2] = new AsyncCallback(Callback);//倒数第2个参数 
  621.                 parmsBeginInvoke[parmsBeginInvoke.Length - 1] = del;                        //倒数第1个参数 
  622.  
  623.                 //重置状态 
  624.                 IsBusy = true
  625.                 Cancelled = false
  626.                 exception = null
  627.                 isCallBackCompleted = false
  628.  
  629.                 waitForm = fmWait; 
  630.                 fmWait.UserCancelling += WaitForm_UserCancelling;//注册用户取消任务事件 
  631.  
  632.                 beginInvoke.Invoke(del, parmsBeginInvoke); 
  633.  
  634.                 if (!isCallBackCompleted)//这里要判断一下,极端情况下有可能还没等ShowDialog,回调就执行完了 
  635.                 { 
  636.                     fmWait.ShowDialog(); //务必确保ShowDialog不会抛异常 
  637.                 } 
  638.  
  639.                 //返回 
  640.                 if (Cancelled) { throw new WorkCancelledException(); } 
  641.                 if (exception != null) { throw exception; } 
  642.                 return result; 
  643.             } 
  644.             finally 
  645.             { 
  646.                 Release(); 
  647.                 UserCancelling = false
  648.                 IsBusy = false
  649.             } 
  650.         } 
  651.  
  652.         /// <summary> 
  653.         /// 回调方法 
  654.         /// </summary> 
  655.         private static void Callback(IAsyncResult ar) 
  656.         { 
  657.             try 
  658.             { 
  659.                 if (Cancelled) { return; } //若任务取消就不必EndInvoke了 
  660.  
  661.                 MethodInfo endInvoke = ar.AsyncState.GetType().GetMethod("EndInvoke"); 
  662.                 object[] parmsEndInvoke = new object[endInvoke.GetParameters().Length]; 
  663.  
  664.                 if (parmsEndInvoke.Length != 1)//若方法存在ref或out参数,赋值给endInvoke参数 
  665.                 { 
  666.                     int i = 0
  667.                     foreach (ParameterInfo p in parmsMethod) 
  668.                     { 
  669.                         if (p.ParameterType.IsByRef) { parmsEndInvoke[i++] = parmsInput[p.Position]; } 
  670.                     } 
  671.                 } 
  672.                 parmsEndInvoke[parmsEndInvoke.Length - 1] = ar; 
  673.  
  674.                 result = endInvoke.Invoke(ar.AsyncState, parmsEndInvoke); 
  675.  
  676.                 if (parmsEndInvoke.Length != 1)//从endInvoke参数取出值返给输入参数 
  677.                 { 
  678.                     int i = 0
  679.                     foreach (ParameterInfo p in parmsMethod) 
  680.                     { 
  681.                         if (p.ParameterType.IsByRef) { parmsInput[p.Position] = parmsEndInvoke[i++]; } 
  682.                     } 
  683.                 } 
  684.             } 
  685.             catch (TargetInvocationException ex) 
  686.             { 
  687.                 exception = ex.InnerException; 
  688.             } 
  689.             catch (Exception ex) 
  690.             { 
  691.                 exception = ex; 
  692.             } 
  693.             finally 
  694.             { 
  695.                 //这里不能用waitForm.Hide()关闭等待窗体,因为回调方法仍然是用后台线程执行,不能操作UI 
  696.                 waitForm.DialogResult = DialogResult.OK; 
  697.                 isCallBackCompleted = true
  698.             } 
  699.         } 
  700.  
  701.         /// <summary> 
  702.         /// 用户请求取消任务时 
  703.         /// </summary> 
  704.         private static void WaitForm_UserCancelling(object sender, EventArgs e) 
  705.         { 
  706.             UserCancelling = true
  707.         } 
  708.  
  709.         /// <summary> 
  710.         /// 释放资源 
  711.         /// </summary> 
  712.         private static void Release() 
  713.         { 
  714.             parmsInput = null;//这里不会影响调用者传入的object[]实例,因为不是ref进来的 
  715.             parmsMethod = null
  716.             IDisposable disp; 
  717.             if ((disp = waitForm as IDisposable) != null) { disp.Dispose(); } 
  718.         } 
  719.     } 
  720.  
  721.     /// <summary> 
  722.     /// 等候窗体为空 
  723.     /// </summary> 
  724.     public class WaitFormNullException : ApplicationException 
  725.     { 
  726.         public WaitFormNullException() : base("等待窗体不能为null!") { } 
  727.     } 
  728.  
  729.     /// <summary> 
  730.     /// 任务正在执行 
  731.     /// </summary> 
  732.     public class WorkIsBusyException : InvalidOperationException 
  733.     { 
  734.         public WorkIsBusyException() : base("任务正在执行!") { } 
  735.     } 
  736.  
  737.     /// <summary> 
  738.     /// 任务已被取消 
  739.     /// </summary> 
  740.     public class WorkCancelledException : ApplicationException 
  741.     { 
  742.         public WorkCancelledException() : base("任务已被取消!") { } 
  743.     } 
  744.  
  745. WaitUI.cs 

WaitForm.cs包含interface IWaitForm和class WaitForm,其中WaitForm为了实现屏蔽关闭按钮,使用了WinFormHelper.cs

  1. using System; 
  2. using System.Windows.Forms; 
  3.  
  4. namespace AhDung.WinForm 
  5.     /// <summary> 
  6.     /// 等待窗体 
  7.     /// </summary> 
  8.     ///<remarks>IWaitForm的默认实现</remarks> 
  9.     public class WaitForm : Form, IWaitForm 
  10.     { 
  11.         /// <summary> 
  12.         /// Required designer variable. 
  13.         /// </summary> 
  14.         private readonly System.ComponentModel.IContainer components = null
  15.  
  16.         /// <summary> 
  17.         /// Clean up any resources being used. 
  18.         /// </summary> 
  19.         /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param> 
  20.         protected override void Dispose(bool disposing) 
  21.         { 
  22.             if (disposing && (components != null)) 
  23.             { 
  24.                 components.Dispose(); 
  25.             } 
  26.             base.Dispose(disposing); 
  27.         } 
  28.  
  29.         #region Windows Form Designer generated code 
  30.  
  31.         /// <summary> 
  32.         /// Required method for Designer support - do not modify 
  33.         /// the contents of this method with the code editor. 
  34.         /// </summary> 
  35.         private void InitializeComponent() 
  36.         { 
  37.             this.lbMsg = new System.Windows.Forms.Label(); 
  38.             this.bar = new System.Windows.Forms.ProgressBar(); 
  39.             this.btnCancel = new System.Windows.Forms.Button(); 
  40.             this.SuspendLayout(); 
  41.             //  
  42.             // lbMsg 
  43.             //  
  44.             this.lbMsg.Location = new System.Drawing.Point(1020); 
  45.             this.lbMsg.Name = "lbMsg"
  46.             this.lbMsg.Size = new System.Drawing.Size(38655); 
  47.             this.lbMsg.TabIndex = 0
  48.             this.lbMsg.Text = "正在处理,请稍候..."
  49.             //  
  50.             // bar 
  51.             //  
  52.             this.bar.Location = new System.Drawing.Point(1278); 
  53.             this.bar.Name = "bar"
  54.             this.bar.Step = 1
  55.             this.bar.Size = new System.Drawing.Size(38416); 
  56.             this.bar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; 
  57.             this.bar.TabIndex = 1
  58.             //  
  59.             // btnCancel 
  60.             //  
  61.             this.btnCancel.Location = new System.Drawing.Point(321109); 
  62.             this.btnCancel.Name = "btnCancel"
  63.             this.btnCancel.Size = new System.Drawing.Size(7523); 
  64.             this.btnCancel.TabIndex = 2
  65.             this.btnCancel.Text = "取消"
  66.             this.btnCancel.UseVisualStyleBackColor = true
  67.             this.btnCancel.Visible = false
  68.             //  
  69.             // FmWaitForDesign 
  70.             //  
  71.             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 
  72.             this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 
  73.             this.ClientSize = new System.Drawing.Size(408155); 
  74.             this.Controls.Add(this.btnCancel); 
  75.             this.Controls.Add(this.bar); 
  76.             this.Controls.Add(this.lbMsg); 
  77.             this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 
  78.             this.MaximizeBox = false
  79.             this.Name = "FmWaitForDesign"
  80.             this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 
  81.             this.Text = "请稍候..."
  82.             this.ResumeLayout(false); 
  83.         } 
  84.  
  85.         #endregion 
  86.  
  87.         System.Windows.Forms.Label lbMsg; 
  88.         System.Windows.Forms.Button btnCancel; 
  89.         System.Windows.Forms.ProgressBar bar; 
  90.  
  91.         public WaitForm() 
  92.         { 
  93.             InitializeComponent(); 
  94.  
  95.             btnCancel.Click += btnCancel_Click; 
  96.             this.FormClosing += WaitForm_FormClosing; 
  97.         } 
  98.  
  99.         #region 将【取消】按钮点击、窗体关闭等行为视为触发【取消任务】事件 
  100.  
  101.         private void WaitForm_FormClosing(object sender, FormClosingEventArgs e) 
  102.         { 
  103.             if (e.CloseReason == CloseReason.UserClosing) 
  104.             { 
  105.                 e.Cancel = true
  106.                 this.OnUserCancelling(); 
  107.             } 
  108.         } 
  109.  
  110.         private void btnCancel_Click(object sender, EventArgs e) 
  111.         { 
  112.             this.btnCancel.Enabled = false
  113.             this.OnUserCancelling(); 
  114.         } 
  115.  
  116.         protected virtual void OnUserCancelling() 
  117.         { 
  118.             if (UserCancelling != null) { UserCancelling(this, EventArgs.Empty); } 
  119.         } 
  120.  
  121.         //屏蔽窗体关闭按钮 
  122.         protected override void OnVisibleChanged(EventArgs e) 
  123.         { 
  124.             base.OnVisibleChanged(e); 
  125.             if (this.Visible) { AhDung.WinForm.WinFormHelper.DisableCloseButton(this); } 
  126.         } 
  127.         #endregion 
  128.  
  129.         #region 实现接口 
  130.  
  131.         public string WorkMessage 
  132.         { 
  133.             get { return lbMsg.Text; } 
  134.             set { lbMsg.Text = value; } 
  135.         } 
  136.  
  137.         public bool BarVisible 
  138.         { 
  139.             get { return bar.Visible; } 
  140.             set { bar.Visible = value; } 
  141.         } 
  142.  
  143.         public ProgressBarStyle BarStyle 
  144.         { 
  145.             get { return bar.Style; } 
  146.             set { bar.Style = value; } 
  147.         } 
  148.  
  149.         public int BarValue 
  150.         { 
  151.             get { return bar.Value; } 
  152.             set { bar.Value = value; } 
  153.         } 
  154.  
  155.         public int BarStep 
  156.         { 
  157.             get { return bar.Step; } 
  158.             set { bar.Step = value; } 
  159.         } 
  160.  
  161.         public void BarPerformStep() 
  162.         { 
  163.             bar.PerformStep(); 
  164.         } 
  165.  
  166.         public bool CancelControlVisible 
  167.         { 
  168.             get { return btnCancel.Visible; } 
  169.             set { btnCancel.Visible = value; } 
  170.         } 
  171.  
  172.         public int BarMaximum 
  173.         { 
  174.             get { return bar.Maximum; } 
  175.             set { bar.Maximum = value; } 
  176.         } 
  177.  
  178.         public int BarMinimum 
  179.         { 
  180.             get { return bar.Minimum; } 
  181.             set { bar.Minimum = value; } 
  182.         } 
  183.  
  184.         public event EventHandler UserCancelling; 
  185.         #endregion 
  186.     } 
  187.  
  188.     /// <summary> 
  189.     /// 等待窗体规范 
  190.     /// </summary> 
  191.     public interface IWaitForm 
  192.     { 
  193.         #region 用于操作等待窗体UI表现的属性和方法,实现时不用操心线程问题,让客户端(任务执行器)去操心 
  194.  
  195.         /// <summary> 
  196.         /// 获取或设置进度描述 
  197.         /// </summary> 
  198.         /// <remarks>建议默认值为“请稍候...”之类的字眼</remarks> 
  199.         string WorkMessage { get; set; } 
  200.  
  201.         /// <summary> 
  202.         /// 获取或设置进度条的可见性 
  203.         /// </summary> 
  204.         /// <remarks>建议默认值为true</remarks> 
  205.         bool BarVisible { get; set; } 
  206.  
  207.         /// <summary> 
  208.         /// 获取或设置进度条的动画样式 
  209.         /// </summary> 
  210.         /// <remarks>建议默认值为Marquee</remarks> 
  211.         ProgressBarStyle BarStyle { get; set; } 
  212.  
  213.         /// <summary> 
  214.         /// 获取或设置进度条的值 
  215.         /// </summary> 
  216.         /// <remarks>建议默认值为0</remarks> 
  217.         int BarValue { get; set; } 
  218.  
  219.         /// <summary> 
  220.         /// 获取或设置进度条的步进幅度 
  221.         /// </summary> 
  222.         int BarStep { get; set; } 
  223.  
  224.         /// <summary> 
  225.         /// 使进度条步进 
  226.         /// </summary> 
  227.         void BarPerformStep(); 
  228.  
  229.         /// <summary> 
  230.         /// 获取或设置取消任务的控件的可见性 
  231.         /// </summary> 
  232.         /// <remarks>建议默认值为false</remarks> 
  233.         bool CancelControlVisible { get; set; } 
  234.  
  235.         /// <summary> 
  236.         /// 获取或设置进度条的值上限 
  237.         /// </summary> 
  238.         /// <remarks>建议默认值为100</remarks> 
  239.         int BarMaximum { get; set; } 
  240.  
  241.         /// <summary> 
  242.         /// 获取或设置进度条的值下限 
  243.         /// </summary> 
  244.         /// <remarks>建议默认值为0</remarks> 
  245.         int BarMinimum { get; set; } 
  246.  
  247.         #endregion 
  248.  
  249.         /// <summary> 
  250.         /// 显示模式等待窗体 
  251.         /// </summary> 
  252.         /// <remarks>建议使用Form类的默认实现</remarks> 
  253.         DialogResult ShowDialog(); 
  254.  
  255.         #region Invoke相关,供客户端在后台线程中操作窗体UI 
  256.  
  257.         /// <summary> 
  258.         /// 指示是否需要使用Invoke操作窗体控件 
  259.         /// </summary> 
  260.         /// <remarks>建议使用Form类的默认实现</remarks> 
  261.         bool InvokeRequired { get; } 
  262.  
  263.         /// <summary> 
  264.         /// 窗体Invoke方法 
  265.         /// </summary> 
  266.         /// <remarks>建议使用Form类的默认实现</remarks> 
  267.         object Invoke(Delegate method); 
  268.  
  269.         /// <summary> 
  270.         /// 窗体BeginInvoke方法 
  271.         /// </summary> 
  272.         /// <remarks>建议使用Form类的默认实现</remarks> 
  273.         IAsyncResult BeginInvoke(Delegate method); 
  274.  
  275.         #endregion 
  276.  
  277.         #region 用于隐藏等待窗体,调用时二选一即可 
  278.  
  279.         //之所以要同实现DialogResult属性和Hide方法,是因为在客户端中需要隐藏窗体时,执行隐藏操作的线程是否UI线程要区别对待, 
  280.         //是UI线程则选用Hide()方法,否则选用DialogResult属性 
  281.  
  282.         /// <summary> 
  283.         /// 获取或设置窗体对话框结果 
  284.         /// </summary> 
  285.         /// <remarks>建议使用Form类的默认实现</remarks> 
  286.         DialogResult DialogResult { get; set; } 
  287.  
  288.         /// <summary> 
  289.         /// 隐藏等待窗体 
  290.         /// </summary> 
  291.         /// <remarks>建议使用Form类的默认实现</remarks> 
  292.         void Hide(); 
  293.  
  294.         #endregion 
  295.  
  296.         /// <summary> 
  297.         /// 当用户请求取消任务时 
  298.         /// </summary> 
  299.         /// <remarks>应在用户交互取消控件、关闭窗体时触发该事件</remarks> 
  300.         event EventHandler UserCancelling; 
  301.     } 
  302.  
  303. WaitForm.cs

可怜的.net 2.0~3.5没有足够的内置委托,所以你可能还需要Delegates.cs

  1. namespace System 
  2.     //无返回委托 
  3.     public delegate void Action(); 
  4.     //public delegate void Action<in T>(T arg);//这个2.0是有的 
  5.     public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2); 
  6.     public delegate void Action<in T1, in T2, in T3>(T1 arg1, T2 arg2, T3 arg3); 
  7.     public delegate void Action<in T1, in T2, in T3, in T4>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); 
  8.     public delegate void Action<in T1, in T2, in T3, in T4, in T5>(T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5); 
  9.     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); 
  10.     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); 
  11.     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); 
  12.     //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); 
  13.     //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); 
  14.     //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); 
  15.     //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); 
  16.     //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); 
  17.     //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); 
  18.     //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); 
  19.     //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); 
  20.  
  21.     //有返回委托 
  22.     public delegate TResult Func<out TResult>(); 
  23.     public delegate TResult Func<in T, out TResult>(T arg); 
  24.     public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2); 
  25.     public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3); 
  26.     public delegate TResult Func<in T1, in T2, in T3, in T4, out TResult>(T1 arg1, T2 arg2, T3 arg3, T4 arg4); 
  27.     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); 
  28.     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); 
  29.     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); 
  30.     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); 
  31.     //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); 
  32.     //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); 
  33.     //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); 
  34.     //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); 
  35.     //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); 
  36.     //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); 
  37.     //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); 
  38.     //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); 
  39.  
  40. Delegates.cs

下面的内容献给闲的蛋疼的童鞋,又或者你想鄙视、教育我这里不该怎样怎样,那里应该怎样怎样的话,请看完再继续。

设计说明:

  • 为什么要把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,这货请参看使用示例中的注释,作为公开成员可有可无

最后真心希望路过大虾对方案不妥之处指点一二,在此谢过。

 

 
责任编辑:王雪燕 来源: 博客园
相关推荐

2018-09-21 10:57:16

2013-03-15 10:37:08

C#

2015-01-04 15:36:52

XSS漏洞XSS

2019-04-11 14:16:31

数据产品经理数据科学数据

2022-01-19 18:02:42

chrome插件使用者

2022-04-29 08:41:40

开发应用程序执行器

2009-09-07 06:18:57

C#窗体设计器

2022-05-05 08:43:22

SQL执行器参数

2020-09-17 06:51:58

OkHttp压缩故障

2009-08-10 14:23:39

C# Setting

2009-08-20 10:10:55

C#透明窗体

2020-10-16 08:26:07

JavaScript开发技术

2018-01-19 15:38:16

戴尔

2017-07-13 17:00:17

内置执行器开发

2009-09-07 06:07:46

C#窗体设计

2009-08-13 10:42:31

C#窗体拖动事件

2009-09-07 04:59:12

C#模式窗体

2009-09-07 04:19:56

C#窗体事件

2009-09-07 06:31:32

C#窗体移动

2009-08-28 15:58:54

C#窗体里调用
点赞
收藏

51CTO技术栈公众号