很多时候,我们需要在窗体中执行一些耗时比较久的任务。比如:循环处理某些文件,发送某些消息等。。。
单纯的依靠状态栏,用户体验不佳,按下功能按钮后得不到有效的提醒,小白用户绝对会电话给你说“我点了以后就没反应了...”。
因此,对于响应时间可能超过5秒以上的事件,有必要增加一个显眼的提示框(在屏幕中央,最好有动图,如果有需要的话还可以设置为模态)。
此实现大体分三部分(重点在于使用BGWK解决UI阻塞的问题):
1、设计好提醒页面(一个转圈圈的动图Image,一个状态文字Label,再加一个进度条):
如果不需要动图,则这个窗体无需编写代码。
2、显示这个窗体的代码,目前放在基类窗体中被子类窗体继承。
1 #region 显示应用程序作业状态 2 /// <summary> 3 /// 后台作业线程定义模板类 4 /// </summary> 5 protected class BgwkDef 6 { 7 public BackgroundWorker TagBgwk; 8 public Action RunningAction; 9 public int TProgMinimum = 0; 10 public int TProgStep = 1; 11 public int TProgMaximum = 100; 12 public string RunningStatus; 13 } 14 15 /// <summary> 16 /// 以指定的定义开始一个线程运行作业任务 17 /// </summary> 18 /// <param name="sBgwkDef"></param> 19 protected void BeginBgwork(BgwkDef sBgwkDef) 20 { 21 if (frmStatus == null) 22 { 23 frmStatus = new FrmStatus(); 24 } 25 if (frmStatus != null) 26 { 27 frmStatus.ProgMain.Minimum = sBgwkDef.TProgMinimum; 28 frmStatus.ProgMain.Step = sBgwkDef.TProgStep; 29 frmStatus.ProgMain.Maximum = sBgwkDef.TProgMaximum; 30 frmStatus.TopLevel = false; 31 frmStatus.Parent = this; 32 frmStatus.Show(); 33 frmStatus.BringToFront(); 34 frmStatus.Left = (this.Width - frmStatus.Width) / 2; 35 frmStatus.Top = (this.Height - frmStatus.Height) / 2 - 90; 36 } 37 if (sBgwkDef.RunningAction == null) 38 { 39 MyMsg.Warning("系统后台任务必须指定作业方法,请检查!"); 40 return; 41 } 42 43 BackgroundWorker tagBgwk = sBgwkDef.TagBgwk ?? new BackgroundWorker(); 44 tagBgwk.WorkerSupportsCancellation = true; 45 tagBgwk.WorkerReportsProgress = true; 46 tagBgwk.DoWork -= BgwkBase1_DoWork; 47 tagBgwk.DoWork += BgwkBase1_DoWork; 48 tagBgwk.ProgressChanged -= BgwkBase1_ProgressChanged; 49 tagBgwk.ProgressChanged += BgwkBase1_ProgressChanged; 50 tagBgwk.RunWorkerCompleted -= BgwkBase1_RunWorkerCompleted; 51 tagBgwk.RunWorkerCompleted += BgwkBase1_RunWorkerCompleted; 52 tagBgwk.RunWorkerAsync(sBgwkDef.RunningAction); 53 } 54 55 /// <summary> 56 /// 取消后台任务的当前作业 57 /// </summary> 58 /// <param name="tagBgwk"></param> 59 protected void CancelBgwork(BackgroundWorker tagBgwk) 60 { 61 tagBgwk.CancelAsync(); 62 } 63 64 /// <summary> 65 /// 在此事件中调用工作方法 66 /// </summary> 67 /// <param name="sender"></param> 68 /// <param name="e"></param> 69 protected void BgwkBase1_DoWork(object sender, DoWorkEventArgs e) 70 { 71 ((Action)e.Argument).Invoke(); 72 } 73 74 /// <summary> 75 /// 当后台任务运行进行进度报告时在状态窗口中显示状态 76 /// </summary> 77 /// <param name="sender"></param> 78 /// <param name="e"></param> 79 protected void BgwkBase1_ProgressChanged(object sender, ProgressChangedEventArgs e) 80 { 81 if (frmStatus != null) 82 { 83 frmStatus.ProgMain.Value = e.ProgressPercentage > frmStatus.ProgMain.Maximum ? frmStatus.ProgMain.Maximum : e.ProgressPercentage; 84 frmStatus.ProgMain.PerformStep(); 85 frmStatus.LabMessage.Text = e.UserState.ToString(); 86 frmStatus.LabMessage.Refresh(); 87 } 88 SetMainStatus(e.UserState.ToString()); 89 } 90 91 /// <summary> 92 /// 任务结束后(e.ProgressPercentage到100)关闭状态窗口 93 /// </summary> 94 /// <param name="sender"></param> 95 /// <param name="e"></param> 96 protected void BgwkBase1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 97 { 98 if (e.Cancelled) 99 { 100 } 101 if (frmStatus != null) 102 { 103 frmStatus.Close(); 104 frmStatus = null; 105 } 106 } 107 #endregion
3、调用第2步的显示方法的代码(实际工作场合)。
private void CmdExport_Click(object sender, EventArgs e) { //创建或匹配一个BackgroundWorker,初始化一个耗时任务 BackgroundWorker bgwk = new BackgroundWorker(); BgwkDef bgwkDef = new BgwkDef() { RunningAction = delegate () { ExportIvoice(bgwk); }, TagBgwk = bgwk }; BeginBgwork(bgwkDef); } //耗时比较久的工作任务方法 private void ExportIvoice(BackgroundWorker bgwk) { bgwk.ReportProgress(1, "正在尝试导出到EXCEL..."); //打开Excel等操作 //各种操作...... if ("出错了") { CancelBgwork(bgwk);//取消任务 MyMsg.Exclamation("开启报表文件失败,请检查!"); return; } //继续运行...... //最后完成任务 bgwk.ReportProgress(100, "导出成功!"); return; }
这样,比较简单的就完成了一个友好提示功能。