這是自己平時根據自己需要寫的一些小代碼,未必對大家有用。另外,這是根據個人想法而寫,未必嚴謹和符合設計原則,若有任何不妥之處,還請不吝賜教。
提示表單在所有軟體中都是必不可少的。其特徵在於按需顯示一個對話方塊,原操作介面被禁用,程式繼續運行;工作完成後,再將對話方塊關閉。
看似很簡單的一個功能,但由於涉及到了線程、表單之間的微妙關係,其實現要比想象中的要複雜得多。
最簡單的辦法是,直接在工作中調用Form.Show顯示提示表單,然後繼續工作,完成後調用Form.Close關閉提示表單。使用這個方法的程式看起來能夠正常工作,不過,由於非模態視窗與建立視窗共用視窗線程,因此,實際上,調用Show後,雖然提示表單能夠顯示,程式也沒有被阻止,但由於視窗線程仍在執行工作,因此提示視窗將無法響應,其介面也將無法重新整理。
既然不能在同一個線程中,那自然就建立個線程來顯示了。
FrmFlexLabelTest dialog = new FrmFlexLabelTest();Thread th = new Thread(new ThreadStart(dialog.Show));th.Start();Thread.Sleep(5000);dialog.Invoke(new ThreadStart(dialog.Close));
看起來挺好一段代碼,不過執行後就會發現問題所在。程式執行後,彈出表單一閃即逝,主表單等待5秒後恢複正常。仔細一想,就不難發現問題所在:由於Show是非阻止函數,線程th在執行完dialog.Show使表單顯示出來之後就立即退出了,而由於dialog線上程th上顯示,th的終止也導致了dialog的關閉。
然後,這裡有一點小小的疑問。調用了dialog.Show的線程為什麼沒有成為表單線程呢?哈,突然想到了Program.cs的裡那句Application.Run,找出來看了一下提示:在當前線程上建立一個標準訊息迴圈…。果不其然,看來問題在這個,那就改在th上調用Application.Run好了:
private void button4_Click(object sender, EventArgs e) { FrmFlexLabelTest dialog = new FrmFlexLabelTest(); Thread th = new Thread(new ParameterizedThreadStart(this.Run)); th.Start(dialog); Thread.Sleep(5000); dialog.Invoke(new ThreadStart(dialog.Close)); } public void Run(object obj) { Application.Run(obj as Form); }
再執行一遍,這次終於正常了。彈出表單能夠響應和被關閉,原線程也未阻塞。基本上,用上面的方式就能實現一個用於顯示提示表單的功能介面了。當然,由於ShowDialog是阻塞函數,將Application.Run換成ShowDialog也可達到同樣的效果。
不過,這樣的一個實現雖然可用,但其效率卻讓人難以恭維。使用Show開啟的表單關閉後,表單即被銷毀,同時訊息迴圈的終止也導致表單線程的終止。也就是說,每顯示一次提示表單的代價是建立一個表單、銷毀一個表單、建立一個線程並為其建立訊息佇列。顯然,這樣的代價很難讓人接受。那麼,又應當如何提高改進呢?同樣顯然的是,在一次程式運行期間,提示表單總是要顯示很多次。因此,若能重用表單對象與線程,則可以均分建立、銷毀它們的代價。要達到重用的目的,上述代碼需要做兩個改變:
1. 使用ShowDialog顯示提示表單,使表單對象可重複顯示。
2. 讓線程在迴圈中調用ShowDialog顯示表單,表單關閉後線程即轉入掛起或休眠,直到需要再次顯示表單時被喚醒。
注(疑問):其實ShowDialog也應該是建立一個新線程來作為表單線程的,這麼說來的,建立線程和訊息佇列的代價似乎不可避免。