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
希望有幫助!