摘要: :本文介紹了 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 中獲益。
|