一个简单多线程等待窗口
在Winform程序开发中,经常会遇到长时间任务处理的时候需要显示等待窗口和进度条的情况,如果直接在主窗口UI线程上执行,则窗口会假死和空白,用户体验很不友好。所以相信大家在实践中都会经常实现自己的等待窗口和进度信息显示,这也是一个我经常在项目开发中使用的小工具,方便地引入新项目中使用,特此与大家分享,并希望大家提出建议,一起交流多线程开发中的经验。
既然要在处理任务的时候要保持主窗口和等待窗口的“活动”状态,自然就想到了多线程和异步操作。.NET Framework里已经自带了后台工作线程对象BackgroundWorker和进度条控件,所以实现起来并不难。这个自定义的等待窗口的好处就是已经把常用的显示控件和接口实现好了,同时可以根据需要灵活修改和进一步美化,便于移植到不同项目中。
实现思路就是定义一个窗体,用来显示进度和其它信息,供主窗口来调用。既然等待窗口只是用来显示,所以它在调用的时候用Form.Show()方法,同时将它定义为单例对象,便于不同的代码段来共同操作它。
主要代码摘录如下:
1.等待窗口的主要定义
1 /// <summary> 2 /// 返回单个实例对象 3 /// </summary> 4 /// <returns></returns> 5 public static WaitWindow GetInstance() 6 { 7 if( instance == null || instance.IsDisposed) 8 { 9 instance = new WaitWindow(); 10 } 11 else 12 { 13 instance.label_percent.Text = ""; 14 } 15 return instance; 16 } 17 18 /// <summary> 19 ///请求终止当前系统正在处理的事务 20 /// </summary> 21 public event System.EventHandler OnProcessAbort; 22 23 /// <summary> 24 /// 修改进度条窗口标题 25 /// </summary> 26 /// <returns></returns> 27 public string Caption 28 { 29 set { this.Text = value; } 30 } 31 /// <summary> 32 /// 修改提示信息 33 /// </summary> 34 /// <returns></returns> 35 public string Title 36 { 37 set { this.label_prompt.Text = value; } 38 } 39 /// <summary> 40 /// 修改进度信息 41 /// </summary> 42 public string ProgressInfo 43 { 44 set { this.label_percent.Text = value; } 45 } 46 47 /// <summary> 48 /// 是否开启取消按钮的可用状态(默认开启) 49 /// </summary> 50 public bool IsCancelEnable 51 { 52 set { this.btn_cancel.Enabled = value; } 53 } 54 55 /// <summary> 56 /// 设置/读到是否显示进度条(默认关闭,如开启,则要传递Value以刷新进度条) 57 /// </summary> 58 public bool IsProgressBarVisible 59 { 60 set { this.progressBar.Visible = value; } 61 get { return this.progressBar.Visible; } 62 } 63 /// <summary> 64 /// 设置/读取当前进度条value 65 /// </summary> 66 public int ProgressValue 67 { 68 set { this.progressBar.Value = value; } 69 get { return this.progressBar.Value; } 70 } 71 /// <summary> 72 /// 设置/读取进度条的最大值(默认100) 73 /// </summary> 74 public int ProgressMaxValue 75 { 76 set { this.progressBar.Maximum = value; } 77 get { return this.progressBar.Maximum; } 78 }
2.主窗口的调用和实现
1 //用户是否取消当前任务 2 private bool isUserCancel = false; 3 4 private void btnDoWork_Click(object sender, EventArgs e) 5 { 6 isUserCancel = false; 7 //显示进度窗口 8 WaitWindow waitx = WaitWindow.GetInstance(); 9 waitx.IsProgressBarVisible = true; //开启进度条 10 waitx.ProgressMaxValue = 10; //进度条最大value(结束值) 11 waitx.OnProcessAbort += new EventHandler(waitx_OnProcessAbort); //用户取消事件 12 waitx.Show(); 13 //使用另一线程进行任务处理 14 Thread t = new Thread(new ThreadStart(this.DoLongTimeTask)); 15 t.Start(); 16 } 17 18 void waitx_OnProcessAbort(object sender, EventArgs e) 19 { 20 isUserCancel = true; 21 } 22 //通知进度代理 23 delegate void NotifyProgressHandler(int progressValue); 24 25 //进度通知处理方法 26 void NotifyProgress(int v) 27 { 28 WaitWindow win = WaitWindow.GetInstance(); 29 win.ProgressValue = v; //当前进度 30 string msg = "已完成: " + v + "/" + win.ProgressMaxValue; 31 win.ProgressInfo = msg; 32 } 33 34 //执行长时间任务处理 35 void DoLongTimeTask() 36 { 37 int i = 1; 38 //因为有可能用户会取消,要随时判断取消标记 39 while (i < 11 && !isUserCancel) 40 { 41 Thread.Sleep(1000); //耗时工作 42 Console.WriteLine("current value:" + i); 43 //通知等待窗口当前进度(BeginInvoke) 44 this.BeginInvoke(new NotifyProgressHandler(this.NotifyProgress), i); 45 i++; 46 } 47 //任务处理完毕 48 if (this.InvokeRequired) 49 this.Invoke(new MethodInvoker(this.EndTask)); 50 else 51 this.EndTask(); 52 } 53 54 //任务处理结束(需要关闭等待窗口以及告知处理结果等) 55 void EndTask() 56 { 57 WaitWindow win = WaitWindow.GetInstance(); 58 win.Close(); 59 if (isUserCancel) 60 MessageBox.Show("任务处理已被用户取消,即将退出. ", "任务处理", MessageBoxButtons.OK, MessageBoxIcon.Asterisk); 61 else 62 MessageBox.Show("任务处理已经完成.", "任务处理", MessageBoxButtons.OK, MessageBoxIcon.Information); 63 }
运行效果如下图:
代码下载:WaitWindowApp
欢迎进一步交流:)