WinForm下WaitForm的設計及其實現


在WinForm編程中,前台有時需要執行一段耗時程序,或者需要加載一些數據(特別是在程序啟動時),這時前台界面會死掉,一般來說解決的辦法是采用后台加載技術,例如使用C#自帶的BackgroudWorker組件或者使用異步加載技術(利用C#新的Task類很容易實現),但是有時僅僅是這樣還不夠,進一步的我們需要知道后台進行的實時操作,這就是這篇文章所要說明的:如何在WinForm下實現一個等待窗口WaitForm,能實時顯示后台操作的情況!

首先,思路很簡單,就是新建一個Form窗口,顯示出來,然后在后台設置該窗口內顯示的內容即可,但是這樣做需要以下幾個問題:

1.應該在一個新的輔助線程中顯示窗口,否則等待窗口運行在主線程,這樣如果主線程阻塞時,等待窗口也會阻塞,就不能實時刷新信息;

2.在輔助線程中顯示窗口,為了簡單,最好用ShowDialog,因為如果用Show顯示,顯示完時候線程會自動退出,以后再調用該窗口時,就會出現如下錯誤信息:

這是由於顯示窗口的線程已經退出了。因此要試用ShowDialog將輔助線程阻塞(當然你也可以試用Show,然后在自己寫代碼將線程阻塞)

3.實現上述兩點之后,就意味着需要跨線程對等待窗口進行調用,在WinForm下跨線程調用時不被允許的,當然直接運行代碼是可以執行的,但是在調試模式下會產生不允許跨線程的錯誤:

因為.net檢測到調用的線程不是創建線程,.net不允許跨線程調用控件的原因,本人猜測可能是WinForm設計的問題,因為WinForm並沒有設計成在多線程模式下運行(據說WPF是基於多線程模式設計,估計可以跨線程調用)

解決這一問題的方法是檢查Form的屬性InvokeRequired,當其為True時表示此時是跨線程調用,此時使用Invoke函數調用即可。

4.在調用Invoke函數時,如果窗口沒有創建,程序會拋出窗體句柄沒有創建的異常,此時只有添加一個循環,檢查IsHandleCreated屬性即可。

5.當然在關閉等待窗口時同樣存在跨線程調用問題,解決方法同上。

基於上述分析,設計一個靜態類將等待窗口的控制封裝起來,該類只用Show和Close兩個簡單函數

 1 public static class WaitWin
 2     {
 3         static FormWait form = null;
 4         public static void Show(string waitMsg)
 5         {
 6             if (form == null)
 7             {
 8                 form = new FormWait();
 9                 ThreadPool.QueueUserWorkItem(state =>
10                 {                    
11                     form.ShowDialog();
12                 });
13             }
14             form.Msg = waitMsg;
15         }
16         public static void Close()
17         {
18             if (form != null)
19                 form.Close();
20             form = null;
21         }
22     }

可以看出,在Show函數中,為了避免主線程阻塞,使用線程池調用ShowDialog函數,用以顯示等待窗口。

FormWait窗口設計很簡單,只是在窗體上放一個lable用於顯示等待消息,當然為了更像等待窗口,需要設置一下窗口的屬性:

1 FormBorderStyle = FormBorderStyle.FixedDialog;//固定窗體大小
2 ShowInTaskbar = false;//不在任務欄中顯示
3 ControlBox = false;//隱藏右上角的關閉等按鈕
4 UseWaitCursor = true;//顯示等待光標
5 StartPosition = FormStartPosition.CenterScreen;//在屏幕中間顯示

 

 1 public partial class FormWait : Form
 2     {
 3         Action<string> setMsg;
 4         Action close;
 5         public FormWait()
 6         {
 7             InitializeComponent();
 8             setMsg = invoke;
 9             close = base.Close;
10         }
11         public string Msg
12         {
13             get
14             {
15                 return this.label1.Text;
16             }
17             set
18             {
19                 while (!this.IsHandleCreated) ;
20                 this.Invoke(setMsg, value);
21             }
22         }
23         public new void Close()
24         {
25             while (!this.IsHandleCreated) ;
26             this.Invoke(close);
27         }
28         void invoke(string msg)
29         {
30             this.label1.Text = msg;
31         }
32     }

 

這樣就可以實現實時顯示后台進度情況,而且調用也十分方便

1 private void Form1_Load(object sender, EventArgs e)
2         {
3             for (int i = 1; i <= 5;i++ )
4             {
5                 WaitWin.Show("等待中,第 " + i + "");
6                 Thread.Sleep(1000);
7             }
8             WaitWin.Close();                      
9         }

示例代碼:http://files.cnblogs.com/ILoveSleep/WaitWin.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM