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
源碼下載地址:http://files.cnblogs.com/xiaozhi_5638/ProgressForm.rar
希望有幫助!