.net开发笔记(十八) winform中的等待框
winform中很多任务是需要在后台线程(或类似)中完成的,也就是说,经常容易涉及到UI界面与后台工作线程之间的交互。比如UI界面控制后台工作的执行(启动、暂停、停止等),后台工作进度在UI界面上的显示。前两天一个员工在UI线程中访问数据库,刚开始数据库在局域网中,没感觉到什么,后来将数据库移到了外网,发现问题来了,至于问题原因想必诸位都知晓,更详细的解释请参考本系列博客(四)。后将这方面的东西整理了一下,如下:
执行后台任务时,UI界面应该怎么做?大概分两种情况:(我自己随便给取的名字)
(1)一种是模式等待
也就说,后台工作没有完成之前,UI界面不允许操作,如下图:
图1
如上图所示,红色箭头表示后台跟UI界面的交互。
(2)一种是非模式等待
后台工作没完成之前,UI界面可以操作,这个类似文件拷贝出现的信息框,如下图:
图2
如上图所示,红色箭头表示后台与UI界面的交互。
以上是两种情况,以及对应的结构图,我做了一个Demo,包含两种等待窗体,一种即为“模式等待窗体”,另一种为“非模式等待窗体”。源码在文章结束后有下载地址。先来分别看一下两者的代码:
(1)模式等待窗体
1 public partial class frmWait : Form 2 { 3 bool _run = true; 4 public frmWait() 5 { 6 InitializeComponent(); 7 } 8 public object DoWait(object param) 9 { 10 List<string> list = new List<string>(); 11 int count = (int)param; 12 progressBar1.Maximum = count; 13 14 //---------------------以下代码片段可以使用线程代替 15 ((Action)delegate() 16 { 17 System.Threading.Thread.Sleep(1000); 18 19 for (int i = 0; i < count; ++i) //耗时操作 20 { 21 if (_run) 22 { 23 string s = DateTime.Now.ToLongTimeString(); 24 list.Add(s); 25 this.Invoke((Action)delegate() 26 { 27 if (!IsDisposed) 28 { 29 progressBar1.Value = i; 30 label1.Text = "正在载入字符串 \"" + s + "\""; 31 } 32 }); 33 System.Threading.Thread.Sleep(500); 34 } 35 else 36 { 37 break; 38 } 39 } 40 41 }).BeginInvoke(new AsyncCallback(OnAsync), null); //异步执行后台工作 42 //------------------------ 43 44 ShowDialog(); //UI界面等待 45 return list; //后台工作执行完毕 可以使用结果 46 } 47 private void OnAsync(IAsyncResult ar) 48 { 49 if (_run) //后台工作正常结束 50 DialogResult = DialogResult.OK; 51 } 52 private void frmWait_Load(object sender, EventArgs e) 53 { 54 55 } 56 57 private void button1_Click(object sender, EventArgs e) 58 { 59 _run = false; //UI界面控制后台结束 60 DialogResult = DialogResult.Cancel; 61 } 62 }
如上代码所示,后台任务很简单,就是返回指定数目(param)个字符串,存放在一个list中,使用frmWait也很简单:
1 using (frmWait frmw = new frmWait()) 2 { 3 List<string> list = frmw.DoWait(50) as List<string>; //弹出模式等待窗体 实时更新显示后台工作进度 后台工作结束后 等待窗体消失 UI线程继续执行... 4 MessageBox.Show("加载字符串 " + list.Count + "个"); 5 }
(2)非模式等待窗体
1 public partial class frmNoWait : Form 2 { 3 bool _run = true; 4 public frmNoWait() 5 { 6 InitializeComponent(); 7 } 8 private void OnAsync(IAsyncResult ar) 9 { 10 // ar.AsyncState as List<string> 后台工作执行完毕的结果 11 12 if (_run) //后台工作正常结束 13 this.Invoke((Action)delegate() 14 { 15 Close(); 16 }); 17 } 18 public void DoNoWait(int param) 19 { 20 List<string> list = new List<string>(); 21 int count = (int)param; 22 progressBar1.Maximum = count; 23 24 //-----------------------以下代码片段 可以使用线程代替 25 ((Action)delegate() 26 { 27 try 28 { 29 System.Threading.Thread.Sleep(1000); 30 for (int i = 0; i < count; ++i) //耗时操作 31 { 32 if (_run) 33 { 34 string s = DateTime.Now.ToLongTimeString(); 35 list.Add(s); 36 this.Invoke((Action)delegate() 37 { 38 if (!IsDisposed) 39 { 40 progressBar1.Value = i; 41 label1.Text = "正在载入字符串 \"" + s + "\""; 42 } 43 }); 44 System.Threading.Thread.Sleep(500); 45 } 46 else 47 { 48 break; 49 } 50 } 51 } 52 catch 53 { 54 55 } 56 }).BeginInvoke(new AsyncCallback(OnAsync), list); //异步执行后台工作 57 //---------------------------- 58 59 Show();//UI界面不用等待 60 } 61 private void frmNoWait_Load(object sender, EventArgs e) 62 { 63 Text += (" " + Form1.index++ + "号"); 64 } 65 66 private void button1_Click(object sender, EventArgs e) 67 { 68 Close(); 69 } 70 protected override void OnFormClosing(FormClosingEventArgs e) 71 { 72 base.OnFormClosing(e); 73 _run = false; //UI界面控制后台结束 74 } 75 }
如上代码所示,后台工作开始后,弹出一个非模式对话框,UI界面可以继续操作,也就是说,你可以出现多个frmNoWait窗体,使用很简单,如下:
1 frmNoWait frmnw = new frmNoWait(); 2 frmnw.DoNoWait(50); //弹出窗体 3 //UI界面继续...
至于怎么通知UI界面,后台工作结束了,你可以在OnAsync中完成这个功能。
最后上几张截图:
图3
图4
源码下载地址:https://files.cnblogs.com/xiaozhi_5638/ProgressForm.rar
希望有帮助!