在 WinForms 中,有時要執行耗時的操作,在該操作未完成之前操作使用者介面,會導致使用者介面停止回應。
解決的方法就是新開一個線程,把耗時的操作放到線程中執行,這樣就可以在使用者介面上進行其它操作。
建立線程可以用 Thread 類,可以實現多線程同時操作,簡單的可以通過 BackgroundWorker 類實現。
用 BackgroundWorker 類執行耗時的操作
BackgroundWorker 類在 System.ComponentModel 命名空間下。
VS 的工具箱時有一個 BackgroundWorker 組件,就是這個類。
常用方法
1.RunWorkerAsync
開始執行後台操作。引發 DoWork 事件
2.CancelAsync
請求取消掛起的後台操作。
注意:這個方法是將 CancellationPending 屬性設定為 true,並不會終止後台操作。在後台操作中要檢查 CancellationPending 屬性,來決定是否要繼續執行耗時的操作。
3.ReportProgress
引發 ProgressChanged 事件。
常用屬性
1.CancellationPending
指示應用程式是否已請求取消後台操作。
唯讀屬性,預設為 false,當執行了 CancelAsync 方法後,值為 true。
2.WorkerSupportsCancellation
指示是否支援非同步取消。要執行 CancelAsync 方法,需要先設定該屬性為 true。
3.WorkerReportsProgress
指示是否能報告進度。要執行 ReportProgress 方法,需要先設定該屬性為 true。
常用事件
1.DoWork
調用 RunWorkerAsync 方法時發生。
2.RunWorkerCompleted
後台操作已完成、被取消或引發異常時發生。
3.ProgressChanged
調用 ReportProgress 方法時發生。
在 DoWork 事件處理常式中不操作任何使用介面物件。而應該通過 ProgressChanged 和 RunWorkerCompleted 事件與使用者介面進行通訊。
如果想在 DoWork 事件處理常式中和使用者介面的控制項通訊,可在用 ReportProgress 方法。
ReportProgress(int percentProgress, object userState),可以傳遞一個對象。
ProgressChanged 事件可以從參數 ProgressChangedEventArgs 類的 UserState 屬性得到這個資訊對象。
簡單的程式用 BackgroundWorker 比 Thread 方便,Thread 中和使用者介面上的控制項通訊比較麻煩,需要用委託來調用控制項的 Invoke 或 BeginInvoke 方法,沒有 BackgroundWorker 方便。
使用這個組件其實非常簡單,例如,我們做一個類似如下介面的進度條的小例子,在後台線程中進行耗時運算,同時重新整理介面上的捲軸和提示資訊,運行結束後,彈出處理結果。
在介面上拖入backgroundWorker組件,並響應其三個事件。
代碼如下:
using System;<br />using System.Collections.Generic;<br />using System.ComponentModel;<br />using System.Data;<br />using System.Drawing;<br />using System.Linq;<br />using System.Text;<br />using System.Windows.Forms;</p><p>namespace 多線程小例子<br />{<br /> public partial class Form1 : Form<br /> {<br /> public Form1()<br /> {<br /> InitializeComponent();<br /> }</p><p>//這裡就是通過響應訊息,來處理介面的顯示工作</p><p> private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)<br /> {<br /> this.progressBar1.Value = e.ProgressPercentage;<br /> this.label1.Text = e.UserState.ToString();<br /> this.label1.Update();<br /> }</p><p>//這裡是後台工作完成後的訊息處理,可以在這裡進行後續的處理工作。</p><p> private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)<br /> {<br /> MessageBox.Show("運算終於完成了");<br /> }</p><p>//這裡,就是後台進程開始工作時,調用工作函數的地方。你可以把你現有的處理函數寫在這兒。</p><p> private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)<br /> {<br /> work(this.backgroundWorker1);<br /> }</p><p>//真正的處理工作</p><p> private bool work(BackgroundWorker bk)<br /> {<br /> int tatle =10000;</p><p> for (int i = 0; i < tatle; i++)<br /> {<br /> if (bk.CancellationPending) //這裡判斷一下是否使用者要求取消後台進行,並可以儘早退出。<br /> {<br /> bk.ReportProgress(i, String.Format("當前值是 {0},操作被使用者申請中斷", i));<br /> return false;<br /> }</p><p>//處理的過程中,通過這個函數,向主線程報告處理進度,最好是折算成百分比,與外邊的進度條的最大值必須要對應。這裡,我沒有折算,而是把介面線程的進度條最大值調整為與這裡的總數一致。<br /> bk.ReportProgress(i, String.Format("當前值是 {0} ", i));<br /> }<br /> return true;<br /> }</p><p> private void button2_Click(object sender, EventArgs e)<br /> {</p><p>//使用者要求取消時,就這樣處理一下。有時不太靈喔。</p><p> this.backgroundWorker1.CancelAsync();<br /> }</p><p> private void button1_Click(object sender, EventArgs e)<br /> {</p><p>//這一句,就是讓後台工作開始。</p><p> this.backgroundWorker1.RunWorkerAsync();<br /> }</p><p> private void button3_Click(object sender, EventArgs e)<br /> {<br /> this.Close();<br /> }<br /> }<br />}
BackGroundWorker的使用方法基本是如出一轍的,大家詳細的看一下文中的Demo,應該很快就能上手~