使用 microsoft.net frameworks 創建基于 windows 的應用程序 
 shawn burke 
 microsoft corporation 
 2000年9月 
摘要: :本文介紹了 win 表單這一新的窗體程序包,借助這一程序包,開發人員能夠充分利用 microsoft windows 操作系統所提供的 ui 功能。
目錄
簡介 
 介紹 win forms 
 更好的易學易用性 
 布局 
 gdi+ 
 訪問底層系統 
 結論 
-------------------------------------------------------------------------------- 
 
簡介
目前 web 已成了街談巷議的話題,看起來好像 microsoft® visual studio® 開發系統對創建基于 microsoft windows® 的傳統應用程序的支持有所減弱。實際上,microsoft 對基于 windows 的應用程序開發方面的投資在不斷加大。
win 表單是一個新的窗體程序包,借助這一程序包,開發人員能夠充分利用 microsoft windows® 操作系統所提供的豐富的用戶界面功能,創建基于 windows 的應用程序。win forms 是新的 microsoft®.net 平臺的一個組成部分,它提供了許多新技術,包括通用的應用程序框架、可管理的執行環境、一體化的安全性以及面向對象的設計原則。而且,win forms 全面支持快速簡便地接入 web services 以及建立豐富的基于 ado+ 數據模型的數據感知應用程序。得益于 visual studio 中新的共享開發環境,開發人員能夠使用包括 microsoft visual basic® 和 c# 在內的任何支持 .net 平臺的語言創建 win forms 應用程序。 
 
介紹 win forms
就像剛才所說的,win forms 是專用于 windows 客戶機 ui 編程的 .net framework 的命名空間。它與 asp+ ui 程序包(即 web forms)共享同樣的設計原則,但其類和實現卻全然不同。在 microsoft win32® api 和 web 組件之間沒有魔術般變形的類。就像所有的 .net frameworks 一樣,一致性已成為優先考慮的問題。其目的是為了 win forms 開發人員能夠迅速適應在 web forms 中編寫代碼,反之亦然。例如,所有命名空間都有 button 類,每一個都有文本、默認的 onclick 事件以及 forecolor、backcolor 和 font 屬性。
win forms 的所有控件都基于 system.winforms.control 類。control 已內置了所有基本的 hwnd 功能,并且它能處理我們已經熟悉并喜愛的絕大多數通用 wm_xxxx 消息。richcontrol 由 control 派生而來,其中添加了布局邏輯和繪圖代碼。system.winforms 命名空間中的絕大多數控件實際上都由 richcontrol 派生而來。scrollablecontrol 能夠支持窗口客戶區域的滾動。一般情況下,對滾動功能的支持是通過 containercontrol 實現的,后者由 scrollablecontrol 派生而來,并增加了對管理子控件、焦點問題和跨欄的支持。form 由 containercontrol 派生而來,是 win form 的頂級控件,它帶有控制標題欄、系統菜單、非矩形窗口和默認控件的屬性。usercontrol 也由 containtercontrol 派生而來,是開發人員能夠創建的控件的基本類。usercontrol 一般用于托管其它子控件,但對于外部客戶機來說,它又是作為單個單元出現的。usercontrol 和 form 在 microsoft® visual studio.net 中都有可視設計器,您會找到用于添加和設計由其所派生的類的項。
圖 1. win forms 控件層次結構
既然我們已了解 win forms 的(最)基本方面,讓我們揭開它的面紗,看看其表面下的一些相當不錯的功能。 
 
更好的易學易用性
win forms 的主要目的是盡可能地提高定位到 win32 平臺的開發人員的工作效率。無論是圖形設備界面 (gdi) 還是窗口狀態管理,為 win32 編程通常都是很困難的。例如,類似 ws_border 或 ws_caption 的一些窗口樣式只能在創建窗口時指定或修改。而 ws_visible 或 ws_child 等其它窗口樣式則可以對已創建的窗口進行修改。win forms 盡力消除了這些細微的差別,并確保操作過程始終保持一致性。可以隨時地、不限次序地對 win forms 控件的屬性進行設置,總能產生預期效果。如果改動過程需要創建新的 hwnd,win forms 框架能夠自動地、透明地重新生成窗口,并為其應用相適宜的所有設置。
由控件獲得通知或事件在 win forms 中也要容易得多。win forms 事件都基于稱為 delegates 的一個通用語言運行時功能。delegates 從本質上講是對類型安全的、可靠的函數指針。對于任一控件的任一事件,都可以添加代理處理程序;絕不會強迫您創建派生類以通過替代處理事件,創建事件映射,或僅為處理一個事件而為類的所有事件實施一個接口。也可以通過替代派生類處理事件,但這種方式一般用于控件創建者或更為高級的應用。匯集某一按鈕的 click 事件相當簡單:
public class buttonclickform: system.winforms.form { 
 private system.winforms.button button1; 
 public buttonclickform() { 
 // 創建按鈕 
 button1 = new system.winforms.button(); 
 // 添加處理程序 
 button1.addonclick(new system.eventhandler(button1_click)); 
 // 將按鈕添加到窗體中 
 this.controls.add(button1); 
 } 
private void button1_click(object sender, eventargs e) { 
 messagebox.show("button1 clicked!"); 
 } 
 } 
這里,我們創建了一個按鈕,并添加了一個名為 button1_click 的處理程序方法,通過短短幾行代碼,在單擊該按鈕后,將調用這一方法。請注意,即使處理程序方法被標記為專用,創建這一掛鉤的代碼仍可以使用該方法,單擊按鈕后,按鈕將能夠激活這一方法的事件。
啟動 win forms 項目的過程也得到了簡化。使用 visual studio.net 創建 win forms 項目的過程只會創建一個要編譯的項目文件:form1.cs。沒有頭文件,沒有接口定義文件,沒有引導程序文件,沒有資源文件,沒有庫文件。項目所需的所有信息都包含在窗體的代碼中。這樣做有一個好處:項目由一個簡單的單窗體應用程序擴展到復雜的、帶有多個代碼文件的多窗體應用程序要方便得多。鏈接過程不需要中間對象文件,只有代碼和已構建的、受管理的所有 dll。只要您習慣了這一方法,就能明顯地感覺到創建 .net framework 應用程序和創建 c/c++ 應用程序之間復雜性的不同。因為信息僅僅包含在代碼文件中,在 visual studio.net 環境外創建版本的過程也非常容易,無論是 visual basic 代碼、c# 代碼,還是任何其它語言編寫的針對 .net framework 的代碼。
因為 win forms 建立在通用語言運行時的基礎之上,開發人員可以任選目前針對通用語言運行時的眾多語言中的一種,構建 win32 應用程序。開發人員現在可以使用多種語言編寫 win forms 應用程序(或 web forms 應用程序或 data 應用程序):從 c# 到 cobol 到 eiffel 再到 perl 等等,中間還有很多種(上一次計數是 17 種)。方便易用再加上廣泛的應用場合相得益彰,為開發人員提供了深厚的基礎,使他們能夠迅速有效地使用 win forms 構建實用的應用程序。 
 
布局
如果您曾嘗試創建能夠正常調整大小的窗體,您就會知道這一過程有多么困難。microsoft foundation classes (mfc) 或早期的 visual basic 版本沒有對這一功能提供內置的支持。然而現在只需幾行代碼(通常情況下您甚至不需要編寫這些代碼,因為在設計時就能通過 property browser 實現這些功能!),即可創建能夠正常調整大小的對話框。
基本布局由兩條組成:anchoring 和 docking。richcontrol 有一個 anchor 屬性,它是一種枚舉類型,可以用“或”操作將這些值組合在一起,以說明控件將與其父控件的某一邊保持恒定距離。例如,如果您將一個按鈕置于窗體上,并將 anchor 屬性設置為 anchorstyles.bottomright,則在調整按鈕的大小時,按鈕將與窗體的底邊和右邊保持同一距離。此外,如果將 anchor 設置為 anchorstyles.all,則按鈕的各個邊都與窗體的對應邊保持同一距離,在調整按鈕大小時仍要滿足這些約束條件。
docking 實際上是 anchoring 的一個特殊情況。richcontrol 的 dock 屬性說明控件要將自身固定到其父控件的哪一邊。docking 可以是 top、left、right、bottom 或 fill。在每種情況下,控件都將移動到盡量靠近指定邊,并調整其大小,以填滿那一邊。如果父控件的大小有所調整,這一狀況仍將保持。將一個控件移動到父控件的底端,并將 anchor 設置為 anchorstyle.bottomleftright,可以模擬 docking bottom。在此處的示例中,列表框是 docked left,按鈕與窗體的頂端、左邊和右邊保持恒定距離,由此它們保持了相對位置和大小。下面的示例對話框(圖 2)完全使用 visual studio.net 中的 win forms 設計器創建,只花了兩分鐘的時間,沒有編寫一行代碼。
圖 2. 使用 win forms 設計器所創建的可調整大小的對話框
// resizablesample.cs 
 namespace resizablesamplenamespace { 
using system; 
 using system.drawing; 
 using system.componentmodel; 
 using system.winforms; 
/// <summary> 
 /// resizablesample 的摘要說明。 
 /// </summary> 
 public class resizablesample : system.winforms.form { 
 /// <summary> 
 /// 為 win forms 設計器所要求 
 /// </summary> 
 private system.componentmodel.container components; 
 private system.winforms.button button3; 
 private system.winforms.button button2; 
 private system.winforms.button button1; 
 private system.winforms.listbox listbox1; 
 public resizablesample() { 
 // 為 win form 設計器支持所要求 
 initializecomponent(); 
}
/// <summary> 
 /// 釋放正在使用的所有資源 
 /// </summary> 
 public override void dispose() { 
 base.dispose(); 
 components.dispose(); 
 } 
 
/// <summary> 
 /// 應用程序的主入口點。 
 /// </summary> 
 public static void main(string[] args) { 
 application.run(new resizablesample()); 
 } 
 
/// <summary> 
 /// 設計器支持所要求的方法 — 不要用編輯器 
 /// 修改這一方法的內容 
 /// </summary> 
 private void initializecomponent() 
 { 
 this.components = new system.componentmodel.container(); 
 this.button2 = new system.winforms.button(); 
 this.button3 = new system.winforms.button(); 
 this.button1 = new system.winforms.button(); 
 this.listbox1 = new system.winforms.listbox(); 
 //@design this.traylargeicon = false; 
 //@design this.trayheight = 0; 
 this.text = "resizable dialog"; 
 this.imemode = system.winforms.imemode.off; 
 this.autoscalebasesize = new system.drawing.size(5, 13); 
 this.clientsize = new system.drawing.size(256, 173); 
 button2.location = new system.drawing.point(152, 60); 
 button2.size = new system.drawing.size(92, 32); 
 button2.tabindex = 2; 
 button2.anchor = system.winforms.anchorstyles.topleftright; 
 button2.text = "cancel"; 
 button3.location = new system.drawing.point(152, 120); 
 button3.size = new system.drawing.size(92, 44); 
 button3.tabindex = 3; 
 button3.anchor = system.winforms.anchorstyles.all; 
 button3.text = "filler"; 
 button1.location = new system.drawing.point(152, 8); 
 button1.size = new system.drawing.size(92, 32); 
 button1.tabindex = 1; 
 button1.anchor = system.winforms.anchorstyles.topleftright; 
 button1.text = "ok"; 
 listbox1.size = new system.drawing.size(120, 173); 
 listbox1.dock = system.winforms.dockstyle.left; 
 listbox1.tabindex = 0; 
 listbox1.items.all = new object[] {"item one", 
 "item two", 
 "item three", 
 "item four"}; 
 this.controls.add(button3); 
 this.controls.add(button2); 
 this.controls.add(button1); 
 this.controls.add(listbox1); 
 } 
} 
 } 
 
gdi+
win forms 全面利用了 gdi+ 這一 microsoft 下一代的二維圖形系統。win forms 中的圖形編程模式完全是面向對象的,各式各樣的畫筆、筆刷、圖像和其它圖形對象與 .net framework 的其它部分一樣,遵循了簡單易用的指導方針。開發人員目前可以使用相當不錯的一些繪圖新功能,如 alpha 混色、漸變色、紋理、消除鋸齒以及采用除位圖外的其它圖像格式。與 windows 2000 操作系統分層和透明的窗口功能配合使用,開發人員能夠毫不費力地創建豐富的、更為圖形化的 win32 應用程序。
如果觸發了控件的 onpaint 事件,能夠由 painteventargs 訪問的 system.drawing.graphics 對象就成為一個 gdi+ 圖形對象。圖形對象能夠執行的所有操作都通過 gdi+ 實施。作為一個示例,使用 gdi+ 創建一個繪制漸變背景的按鈕。
圖 3. 使用 gdi+ 創建的按鈕
以下是實現這一按鈕的代碼:
public class gradientbutton : button { 
 // 保留顏色設置的成員 
 private color startcolor; 
 private color endcolor; 
// 書寫文字時我們將需要它 
 private static stringformat format = new stringformat(); 
 public gradientbutton() : base() { 
 // 初始化顏色 
 startcolor = systemcolors.inactivecaption; 
 endcolor = systemcolors.activecaption; 
 format.alignment = stringalignment.center; 
 format.linealignment = stringalignment.center; 
 } 
/// <summary> 
 /// 漸變色的終止顏色 
 // </summary> 
 public color endcolor { 
 get { 
 return this.endcolor; 
 } 
 set { 
 this.endcolor = value; 
 // 如有必要,則導致重新繪制 
 if (this.ishandlecreated && this.visible) { 
 invalidate(); 
 } 
 } 
 } 
 
/// <summary> 
 /// 漸變色的起始顏色 
 // </summary> 
 public color startcolor { 
 get { 
 return this.startcolor; 
 } 
 set { 
 this.startcolor = value; 
 // 如有必要,則導致重新繪制 
 if (this.ishandlecreated && this.visible) { 
 invalidate(); 
 } 
 } 
 } 
protected override void onpaint(painteventargs pe) { 
 // 繪制按鈕的常規背景以形成 
 // 邊框,等等。 
 base.onpaint(pe); 
 graphics g = pe.graphics; 
 rectangle clientrect = this.clientrectangle; 
 // 縮小矩形,以免繪制時出界 
 clientrect.inflate(-1,-1); 
 // 創建漸變筆刷,從 
 // 左上角運行到右下角。 
 brush backgroundbrush = new lineargradientbrush( 
 new point(clientrect.x,clientrect.y), 
 new point(clientrect.width, clientrect.height), 
 startcolor, 
 endcolor); 
 // 以漸變色填充背景.... 
 g.fillrectangle(backgroundbrush, clientrect); 
 // 在客戶機區域的中間書寫文字。 
 g.drawstring(this.text, 
 this.font, 
 new solidbrush(this.forecolor), 
 clientrect, 
 format); 
 } 
 } 
就像您所看到的,這并不是非常困難。得益于 win forms 和 gdi+ 面向對象的設計,無需編寫任何復雜的代碼,即可實現我們的 gradientbutton,并且在設計器中,可以通過 property browser 操作 text、font、startcolor 和 endcolor。 
 
訪問底層系統
許多框架的一個缺點就是:如果人們編寫的應用程序類型與示例和演示中的嚴格一致,則這些框架的效果相當不錯,但有時開發人員發現,一旦他們希望用框架進行一些有創造性的工作,某些情況下就會碰到障礙或遭到失敗。如果遇到這一情況,win forms 框架自始至終都能夠允許開發人員訪問系統基礎結構。當然,希望 win forms 這樣一個設計優良的框架不會使用戶遭遇這種情況,但可能發生的情況幾乎是無限的。所有的控件都有 handle 屬性,允許訪問控件的窗口句柄 (hwnd),gdi 對象也提供了類似的句柄訪問過程。而且,control 實際上擁有一個名為 wndproc 的受保護的虛擬方法,對于少數 win forms 尚不能支持的消息,可以替代該方法,添加處理方式。
例如,假設您的應用程序是資源密集型的,需要響應 wm_compacting。如果系統檢測到內存不足,會向所有高層窗口廣播 wm_compacting,您就會知道 win forms 框架對這一消息沒有提供內置支持,由此,可以添加如下處理過程:
/// <summary> 
 /// win32form1 的摘要說明。 
 /// </summary> 
 public class compactableform : system.winforms.form { 
 private eventhandler handler; 
 public void addoncompacting(eventhandler h) { 
 handler = (eventhandler) delegate.combine(handler, h); 
 } 
 
protected override void oncompacting(eventargs e) { 
 // 查看運行時系統能否釋放任何東西 
 system.gc.collect(); 
 // 調用任一處理程序。 
 if (handler != null) handler(this, e); 
 } 
public void removeoncompacting(eventhandler h) { 
 handler = (eventhandler) delegate.remove(handler, h); 
 } 
 
protected override void wndproc(ref message m) { 
 case (m.msg) { 
 case win.wm_compacting: 
 oncompacting(eventargs.empty); 
 break; 
 } 
 base.wndproc(m); 
 } 
}
只需數行代碼,當系統試著收集未用資源時,利用新的 compactableform 類或由此派生的類即可得到通知,并作出響應。 
 
結論
盡管在許多開發人員的計劃中,針對 web 的開發是當前工作的重點,而定位于熟悉的 win32 平臺仍然是一個不得不面對的情況。有了 win forms,windows 開發人員無論是新手還是老手,都會發現使用豐富的接口創建復雜的應用程序是一個很方便的過程,而這些接口與 .net framework 中具有 web 和數據功能的許多技術配合良好。
通過利用跨語言繼承、碎片收集和安全性等通用語言運行時提高工作效率的優秀功能,開發人員將從 .net framework 和 win forms 中獲益。
新聞熱點
疑難解答
圖片精選