預建的自定義控件可以簡化和加快應用程序的設計,並使您能夠維護 UI 的一致性。但是,預先打包的控件可能很大,速度很慢,並且是特定於操作系統的。對於不願意使用預先打包的控件的人來說,Visual Studio .NET 提供了類似於 Windows 窗體中的控件的 Web 窗體控件,其中包括標簽和文本框,以及新增的 DataGrid 等,所有這些控件都可以進行自定義。
如果要設計自己的控件,您可以通過使用 .NET 框架所提供的可繼承類來避免繁雜無味的工作過程,包括頁生存周期、在調用之間維護狀態以及浏覽器檢測。本文將對這些概念以及事件處理、呈現和客戶端腳本進行介紹。
控件是一個很吸引人的概念。使用預先打包的 UI 功能時,可以更快、更廉價地完成設計,並能夠在不同的應用程序之間保持更為一致的 UI。但這並不是它的所有特點。控件也可能會很大,與為特定任務而專門編寫的代碼相比,可能運行速度也要慢些。並且,基於 Windows 的控件結構如 Windows 窗體控件和早期的 ActiveX、OCX 和 VBX 控件,僅可以在 Windows 環境中運行 - 在當今幾乎每天都有新的平台類型脫穎而出的異類 Internet 環境中,這很成問題。
為了能夠跨平台運行,Microsoft 設計了Web 窗體結構 ASP.NET。使用 Visual Studio,您可以從工具箱中選擇稱為 Web 窗體控件的組件,並將它們放置在 ASPX 頁上。然後,您可以設置該控件的屬性,並使用任何支持 .NET 的語言編寫代碼,從而將其行為與其他控件相關聯。這個過程被設計為與用 Visual Basic 編寫應用程序類似,對於大多數程序員來說,這都是一個非常熟悉的過程。
圖 1 頁請求
客戶端請求包含 Web 窗體控件的頁時,ASP.NET 處理器將加載該頁並在服務器上創建這些控件,如圖 1 所示,然後執行該頁的編程邏輯,將控件關聯到一起。該過程結束時,每個控件都會向 ASP.NET 提供描述其當前外觀的 HTML。這些 HTML 將被返回到客戶端,並在浏覽器中呈現。請在以下位置閱讀有關 ASP.NET Web 窗體的信息:ASP .NET:Web Forms Let You Drag and Drop Your Way to Powerful Web Apps。
Visual Studio 附帶了一組通用的 Web 窗體控件,或多或少地與 Windows窗體中的可用控件組相對應。它包含標簽和文本框等已為大家所熟悉的常用控件,還包含更新、更為復雜的控件如 DataGrid。
本文介紹了 .NET 框架為編寫自己的 Web 窗體控件而提供的功能。由於我已經在 MSDN® Magazine 的 2002 年 4 月刊中介紹了控件的基本概念(方法、屬性和事件),本文中,我將主要介紹 Web 窗體控件與基於 Windows 的控件之間的區別。之所以存在這些區別,主要是由於在相對簡陋的浏覽器運行庫環境中運行,而不是在資源豐富的Windows 環境中運行。
.NET 框架中包含預先創建的軟件類,這些類使得編寫 Web 窗體控件變得相對簡單。您需要理解 HTML 才可以生成控件所要求的輸出,就像 Windows 窗體控件的設計者需要理解 Windows GDI 一樣。但是,掛鉤到 ASP.NET 頁生存周期的例程、在多次調用之間維護狀態的例程和檢測宿主浏覽器功能的例程(全部控件都具備的基礎結構)已經為您編寫好了。
您可以使用自己所選擇的與 .NET 兼容的語言來編寫控件,通過從自己選擇的若干個 .NET 框架類繼承來使用預先創建的基礎結構。這些基類大致對應於 Windows 窗體中的類。但是,常見的情況是,Microsoft 使用了同一個名稱來在看上去類似的環境中指代不同的事物,因此,必須謹慎從事,並閱讀附屬細則。下面介紹開發 Web 窗體控件的五種方法。
第一種選擇是從 System.Web.UI.Control 派生,該類是所有 Web 窗體控件的基類。它參與 ASP.NET 頁呈現過程的所有生存周期事件。文檔中指出該類沒有任何特定於用戶界面的功能,雖然它存在於 System.Web.UI 命名空間中。我有不同意見。它包含一個 Render 方法(將在下一節對其進行介紹),控件使用該方法來發出要在浏覽器中顯示的 HTML。它比我要介紹的下一個類具有更少的內置 UI 屬性;例如,它沒有 Width、Height、ForeColor、BackColor 和 Font,但如果您一定要使用它來生成 UI,實現起來仍是非常容易的。由於省略了以上元素,它更為輕型化,但由此所造成的差異微乎其微。如果您不關注它所省略的任何功能,使用它作為基類完全沒有問題。
您的下一個選項是從 System.Web.UI.WebControls.WebControl 派生,該類派生自 System.Web.UI.Control。這是一個添加了基本用戶界面屬性的控件。當您生成新的 Web 控件庫項目時,Visual Studio 會自動使用它作為基類。如果您想要得到像大多數控件一樣提供用戶界面的控件,您可能應當從這裡開始。
您還可以從現有的 Web 窗體控件入手,可以是 Visual Studio 附帶的控件,或者從第三方購買的控件。此時,您需要使用 .NET 繼承機制從現有控件派生自己的控件。您需要重用現有功能中自己需要的部分,重寫要更改的部分,並添加要使控件具備的任何附加功能。在我以前的文章中,曾經介紹了如何使用 Windows 窗體文本框控件來完成以上任務。針對 Web 窗體控件完成該任務與此完全相同,這裡不再重復介紹。
另一個選項是設計一個包含其他控件的控件。在 Windows 窗體中,它稱為用戶控件,但是在 Web 窗體中,它稱為復合控件。您可以為自己的基類選擇以上所介紹的三種繼承方案中的任一種。不幸的是,Web 窗體不像 Windows 窗體一樣支持添加子控件。因此,必須在代碼中手動創建和定位子控件。這並不困難,因此這裡不作討論。但是,我發現這一省略頗為令人吃驚。
最後,您可以選擇創建用戶控件。以上所討論的每個控件都是完全編譯的 .NET 程序集。它們可用於 Visual Studio 工具箱和設計器,並可以保存在全局程序集緩存 (GAC) 中,因此無需為每個要使用它們的客戶端使用一個單獨的副本。Web 窗體提供了另外一種生成可重用控件包的方法(用戶控件)。像您在 Windows 窗體中看到的用戶控件一樣,Web 窗體用戶控件是通過在 Visual Studio 設計器中將其他控件放置到設計圖面上來生成的。但是,與 Windows 窗體用戶控件不同,這種用戶控件是一個 HTML 頁,而不是已編譯的程序集,因此不能保存到 GAC 或 Visual Studio 工具箱中,而且,也不能在 Visual Studio 設計器中顯示其外觀。因此,我認為它的有用性遠不如我所介紹的其他類型的 Web 窗體控件。我懷疑它之所以存在,是因為復合 Web 窗體控件中缺少設計器支持。
返回頁首在學習或者講解新軟件時,我總會從我所能想到的最簡單的示例入手。這是一個標簽控件,它包含一個稱為 Text 的屬性,該屬性是標簽所顯示的字符串,它還包含一個稱為 ForeColor 的屬性,即該文本字符串的顏色(參見圖 2)。
圖 2 標簽控件
我首先在 Visual Studio 中生成了該項目,然後從 New Project 對話框中選擇了 Web Control Library,如圖 3 所示。向導生成了一個項目,該項目包含一個派生自 System.Web.UI.WebControls.WebControl 的新類。向該類中添加方法和屬性與向任何其他 .NET 類添加方法和屬性完全相同。實際上,系統提供的基類已經包含了本示例中所使用的稱為 Text 和 ForeColor 的屬性。
圖 3 生成項目
就像我說的一樣,這非常簡單。基本上是這樣的。理解自定義 Web 窗體控件的關鍵在於 Render 方法,該方法在概念上與 Windows 窗體 OnPaint 方法相同,區別僅在於前者發出 HTML,而後者則發出 GDI 調用。當 ASP.NET 服務器框架為響應用戶請求而匯編 Web 窗體頁時,它將創建頁上列出的控件,設置這些控件的屬性和持久性數據,然後調用它們的各個 Render 方法。框架實際上是告訴控件:“你是活動的,並正處於預期的狀態。你需要告訴我你的外觀,因為我沒有別的辦法來了解。”Web 窗體控件的作者會在 Render 方法中放入一些代碼,以發出 HTML,告知浏覽器如何根據控件的當前狀態和屬性,以及與控件有關的環境中的任何其他信息來顯示控件的外觀。
Web 窗體控件的作者需要了解 HTML,這是因為環境提供的相關摘要信息很少。以下是指定文本顏色的 HTML:
<span style="color:green;"> Here is some text </span>
為了生成這些 HTML,我編寫了圖 4 中所示的代碼。大多數讀者都告訴我他們希望源代碼使用 Visual Basic 來編寫;為了照顧 C# 讀者,我使用 Visual Basic 和 C# 兩種語言編寫了可下載的示例代碼。
當 ASP.NET 框架調用控件的 Render 方法時,將傳遞一個 System.Web.UI.HtmlTextWriter 類型的對象。這在概念上類似於 OnPaint 方法在 Windows 窗體控件中收到的 System.Windows.Forms.PaintEventArgs 的 Graphics 成員。兩者都代表到框架的連接,該框架將輸出定位到其相應的位置。HtmlTextWriter 包含的方法、屬性和常數使得您的控件能夠將 HTML 發送到將被發送到客戶端浏覽器的輸出頁上。在示例代碼中,我首先調用了方法 AddStyleAttribute,該方法在內部創建一個稱為 style 的 HTML 屬性,將其值設置為控件所繼承的 ForeColor 屬性的值,然後將其添加到內部緩沖區。可以通過對 AddStyleAttribute 方法的附加調用向緩沖區添加 style 屬性的附加值;通過調用 AddAttribute 方法,可以添加其他屬性,當然,在本例中,並不需要這麼做。
接著,我調用了方法 RenderBeginTag,指定文本中要顯示的 HTML 標記的名稱,本例中為“span”。該調用從內部緩沖區中提取任何屬性(此處為 style),將它們放置到標記中,然後寫入 HTML輸出流中。這兩個調用生成了第一行 HTML:
<span style="color:green;">
接下來,為了編寫標簽的文本,我調用了方法 HtmlTextWriter.Write,以傳遞控件的內部文本字符串。該方法將文本字符串 verbatim 傳遞到 HTML 輸出流中,從而生成了第二行:
Here is some text
為了關閉 標記,我調用了 HtmlTextWriter.RenderEndTag。這導致編寫器讀回最後一個打開的標記,並發出該標記的關閉標記,在本例中為 ,以作為最後一行 HTML。
該對象包含用於執行輸出的其他方法,這些方法能夠提供更為精細的控件,但較為復雜。為簡單起見,我將在本文的其余部分中沿用前面的方法。
圖 5 向工具箱中添加新的控件