這篇文章給大家介紹如何探討WinForm不同代碼的實現(xiàn),內(nèi)容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
站在用戶的角度思考問題,與客戶深入溝通,找到渭濱網(wǎng)站設(shè)計與渭濱網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗,讓設(shè)計與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個性化、用戶體驗好的作品,建站類型包括:成都網(wǎng)站設(shè)計、網(wǎng)站制作、企業(yè)官網(wǎng)、英文網(wǎng)站、手機端網(wǎng)站、網(wǎng)站推廣、空間域名、網(wǎng)站空間、企業(yè)郵箱。業(yè)務(wù)覆蓋渭濱地區(qū)。
在進入正文之前,想請大家先欣賞下面兩段代碼:
//這是一個控制臺程序,請先添加System.Windows.Form.dll的引用 using System.Windows.Form; public class ConsoleApplicationShowDialog { static void Main() { Form frm = new Form(); frm.ShowDialog(); } }
兩個代碼片段都是控制臺程序(編譯的時候,請選擇ConsoleApplication類型編譯)。這兩段程序***的區(qū)別就在于顯示窗體的時候第一個使用ShowDialog(就是所謂的模態(tài)窗體),第二個使用Show(也就是所謂的非模態(tài)窗體)。
經(jīng)過測試我們發(fā)現(xiàn),使用Show顯示出來的窗體一顯示就死在那里了,不響應(yīng)用戶的輸入,如果你在窗體上放一個按鈕,甚至發(fā)現(xiàn)按鈕都無法顯示,點擊也無任何響應(yīng)。而是用ShowDialog顯示出來的窗體卻不一樣,可以響應(yīng)用戶的輸入。這是什么原因呢?
為了找到問題的根源,我們來看看Show方法和ShowDialog方法實現(xiàn)的區(qū)別。Show方法是在Control里定義的,F(xiàn)orm間接的派生自Control類(看起來這里是一個組合模式哦),Show方法代碼:
public void Show() { this.Visible = true; }
Show方法的代碼相當(dāng)?shù)暮唵?,做的工作僅僅就是將窗體顯示出來,那前面第二段代碼應(yīng)該與下面的代碼作用是一樣的:
//這是一個控制臺程序,請先添加System.Windows.Form.dll的引用 using System.Windows.Form; public class ConsoleApplicationShow { static void Main() { Form frm = new Form(); frm.Visible = true; } }
現(xiàn)在再來看看ShowDialog方法,ShowDialog方法有些復(fù)雜,但是在這百來行代碼中,應(yīng)該有一條你很熟悉:
public DialogResult ShowDialog(IWin32Window owner) { //...省略 Application.RunDialog(this); //...省略 }
哦,這行代碼跟我們千千萬萬個WinForm程序的啟動部分相當(dāng)類似:
public class Program { static void Main() { Form frm = new Form(); Application.Run(frm); } }
MSDN對Application.Run的說明是:
Begins running a standard application message loop on the current thread, and makes the specified form visible.在當(dāng)前的線程上啟動一個標(biāo)準(zhǔn)的應(yīng)用程序“消息循環(huán)”,并且顯示指定的窗體,下面是Application.Run的代碼:
public static void Run(Form mainForm){ ThreadContext.FromCurrent().RunMessageLoop(-1, new ApplicationContext(mainForm)); }
哦?什么是消息循環(huán)?如果你是直接進入.Net開發(fā)的,沒有經(jīng)過Win32時代的洗禮,那可能對這個消息循環(huán)并不是很清楚,在你眼里只有注冊事件,處理事件。雖然.Net通過封裝,簡化了消息循環(huán)這種處理用戶點擊等事件的編程模型,但是.Net底下還是Win32,有的時候我們還是得了解一下,對理解有些問題可能有幫助(后面會提到)。
消息循環(huán)(Message Loop)
說Application.Run啟動一個消息循環(huán),那么什么是消息循環(huán)呢?看下面的代碼:
MSG msg; while(GetMessage(&msg,NULL,0,0)){ TranslateMessage(&msg); DispatchMessage(&msg); }
這是一段幾乎所有使用Win32 API編寫Windows Application的程序里都有的代碼。這就是一個消息循環(huán)。你不需要透徹的理解上面這段代碼,你只需要了解這么一個意思:
Windows為每個Windows程序都維護了一個消息隊列,當(dāng)有用戶輸入事件的時候,Windows就把這個事件轉(zhuǎn)換為一個稱之為“消息”的東東(也就是上面代碼中的MSG結(jié)構(gòu)),在這個消息里包含有一些信息,比如鼠標(biāo)點擊的點啊,消息的類型啊等等。而上面的while循環(huán)中的GetMessage方法就是不斷的從這個消息隊列里取消息出來,然后處理,這樣窗體就能響應(yīng)用戶的輸入了。
通過上面的討論,我們現(xiàn)在大概明白了為啥Show和ShowDialog區(qū)別這么大呢,原來ShowDialog啟動了一個消息循環(huán),這樣用ShowDialog顯示出來的窗體就能響應(yīng)用戶的輸入事件了,而Show僅僅是設(shè)置一下窗體的Visible屬性,并沒有啟動一個消息循環(huán),使用Show顯示出來的窗體也就無法響應(yīng)用戶的輸入事件了,也就是死在那里了。
上面說,GetMessage取出消息,然后處理,那在哪兒處理呢?在Win32程序中我們還可以看到這樣的片段:
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam){ switch(message) { case WM_CREATE: //處理窗體創(chuàng)建事件 return 0; case WM_PAINT: //處理窗體繪制事件 return 0; //更多事件,比如按鈕點擊等 } }
啊,好丑陋的處理方式。原來是根據(jù)message的類型,做出不同的處理,而Windows定義了一大堆WM_開頭的東東??晌覀兛蓯鄣?Net,WinForm里面優(yōu)美的事件處理模型就是基于這個之上的,通過上面的代碼,和你在.Net里使用事件的感觸你是否能想象出.Net是如何封裝這個過程的?
WinForm中的消息處理
實際上在.Net的WinForm中,消息處理的影子還是存在的,并沒有消失得無影無蹤,在Form中還有這么一個protected的方法:
protected override void WndProc(ref Message m){ switch (m.Msg) { case 0x10://...... case 0x11: //.... } base.WndProc(ref m);}
哦,原來與Win32里面的那個一模一樣。實際上通過重寫這個方法我們可以實現(xiàn)一些正常做法難以實現(xiàn)的東東。
為什么耗時操作要異步
談了這么多,我們來談一點我們身邊的事情。你應(yīng)該碰到過這樣一個場景:編寫一個程序,點擊一個按鈕之后要做一個比較耗時的操作,比如要更新一大批數(shù)據(jù)到數(shù)據(jù)庫,這個時候程序就像本文開頭那個程序一樣,死掉了。用戶不管怎么點擊,程序變成灰色,標(biāo)題欄上還顯示一個“沒有響應(yīng)”,有的程序甚至連個提示都不給,用戶以為真的死掉了,氣急敗壞的啪嚓一下把程序關(guān)了,耗時操作進行到一半就這樣被無情終止。這是為什么呢?
通過前面的討論,我們知道,響應(yīng)用戶的輸入就是靠消息循環(huán),而消息循環(huán)就是在當(dāng)前的線程上,也就是我們所謂的那個UI線程,如果一個耗時操作也同在UI線程上,那么消息循環(huán)就“卡著”了,也就無法處理后續(xù)的消息,程序也就假死了。
那我們?nèi)绾翁幚磉@種耗時操作呢?當(dāng)然就是將這個耗時操作放到另外一個線程中,不占用UI線程,讓消息循環(huán)得以繼續(xù)的進行下去。
關(guān)于如何探討WinForm不同代碼的實現(xiàn)就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。