- 需求:後臺不間斷地進行某種工作,當獲得特定結果時彈出窗體進行提示,而工作繼續進行。那我的第一反應就是,該工作應放到後臺線程中執行,條件滿足時創建/顯示提示窗體就行啦。代碼如下:
- ==========================Work.cs==============================
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.ComponentModel;
- namespace ThreadTest
- {
- public class Work
- {
- Thread thd;
- private static int count = 0;
- public Work()
- {
- thd = new Thread(new ThreadStart(thdDoWork));
- thd.Name = "NewThread";
- }
- WarningForm wf;//提示窗體
- private void thdDoWork()//後臺工作
- {
- while (true)
- {
- if ((count++ % 10) == 0)
- {
- wf = new WarningForm();//創建並顯示提示窗體
- wf.Show();//將窗體顯示爲非模式對話框
- }
- Thread.Sleep(600);
- }
- }
- public void ThdStart()
- {
- thd.Start();
- }
- }
- }
- ==========================Work.cs==============================
- 同時呢,在WarningForm的構造函數中添加:
- Console.WriteLine("WarningForm Created in : " + Thread.CurrentThread.Name);
- 這樣我們就能夠清楚的看到WarningForm是在哪一個線程中被創建顯示的。
- Debug,彈出提示窗口,而且後臺工作沒有停止,但是提示窗口沒有響應,後臺輸出“WarningForm Created in : NewThread”。我分析這是因爲我們把提示窗體顯示爲了非模式的,而後臺線程顯示窗體後繼續執行,並沒有維護對提示窗體界面的響應,因此發生了以上現象。
- 那我們把"wf.Show()"改爲"wf.ShowDialog()"再試一下:
- Debug,彈出提示窗口,界面沒有死掉,後臺輸出"WarningForm Created in : NewThread",但是後臺的工作卻停止了。這是因爲ShowDialog()將窗體顯示爲模式的,也就是說該窗體的顯示阻塞掉了當前線程的運行。
- 這樣看來,把提示窗體的顯示放到後臺線程中是不行的,那麼,當後臺線程需要顯示窗體時,如何在主線程中捕獲這種消息呢?
- 哈,這位客官運氣真好,BackgroundWorker的ReportProgress(int percentProgress, object userState)方法最適合解決這個問題了。我們可以在主線程中註冊BackgroundWorker的ProgressChanged事件,當BackgroundWorker的對象調用ReportProgress()時,註冊到ProgressChanged事件的方法就會在主線程中執行,對後臺線程的運行沒有任何影響,然後提示窗體是要顯示爲模式的還是非模式的就看情況啦!
- 我們修改Work.cs如下:
- ==========================Work.cs==============================
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.ComponentModel;
- namespace ThreadTest
- {
- public class Work
- {
- public BackgroundWorker bg;
- private static int count = 0;
- public Work()
- {
- bg = new BackgroundWorker();
- }
- private void bgDoWork(object sender, DoWorkEventArgs e)
- {
- Thread.CurrentThread.Name = "BgWorker";//修改後臺線程的名字
- while (true)
- {
- if ((count++ % 10) == 0)
- bg.ReportProgress(count);//激活事件
- Thread.Sleep(500);
- }
- }
- public void BgStart()
- {
- bg.WorkerReportsProgress = true;
- bg.DoWork += new DoWorkEventHandler(bgDoWork);
- bg.RunWorkerAsync();
- }
- }
- }
- ==========================Work.cs==============================
- 同時在Form1.cs中如下編寫:
- ========================Form1.cs片段============================
- WarningForm wf;
- public Form1()
- {
- InitializeComponent();
- Thread.CurrentThread.Name = "MainThread";//修改主線程名字
- }
- private void button3_Click(object sender, EventArgs e)
- {
- //work爲Work的對象
- work.bg.ProgressChanged += new ProgressChangedEventHandler(bg_ProgressChanged);//註冊這一事件
- work.BgStart();
- }
- private void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
- {
- //事件發生時顯示提示窗體
- wf = new WarningForm();
- wf.Show();
- //wf.ShowDialog();
- }
- ========================Form1.cs片段============================
- Debug,輸出"WarningForm Created in : MainThread",可以看到窗體是在主線程中創建顯示的。而無論提示窗體是模式的還是非模式的,一切均能正常運行,到此,我們就實現了要求的功能哈哈!
- 另外,請注意ReportProgress(int percentProgress, object userState),它第二個參數是object類型的,再加上如前所述的特性使得BackgroundWorker與界面進行通信的時非常的方便,我們在更新控件的時候可以用它。而如果使用Thread對象的話,則需要通過委託來調用控件的Invoke或者BeginInvoke方法,麻煩的多。但是我想後者肯定具有一些獨特的優點,目前我還不知道,求指點。。