使用 .NET 框架轻松开发完美的 Web 窗体控件
作者:David S. Platt?? 出自:微軟
本文假定您熟悉 Visual Basic .NET、C# 和 HTML
下載本文的代碼: WebC.exe (274KB)
摘要 預(yù)建的自定義控件可以簡化和加快應(yīng)用程序的設(shè)計,并使您能夠維護 UI 的一致性。但是,預(yù)先打包的控件可能很大,速度很慢,并且是特定于操作系統(tǒng)的。對于不愿意使用預(yù)先打包的控件的人來說,Visual Studio .NET 提供了類似于 Windows 窗體中的控件的 Web 窗體控件,其中包括標簽和文本框,以及新增的 DataGrid 等,所有這些控件都可以進行自定義。
如果要設(shè)計自己的控件,您可以通過使用 .NET 框架所提供的可繼承類來避免繁雜無味的工作過程,包括頁生存周期、在調(diào)用之間維護狀態(tài)以及瀏覽器檢測。本文將對這些概念以及事件處理、呈現(xiàn)和客戶端腳本進行介紹。
控件是一個很吸引人的概念。使用預(yù)先打包的 UI 功能時,可以更快、更廉價地完成設(shè)計,并能夠在不同的應(yīng)用程序之間保持更為一致的 UI。但這并不是它的所有特點??丶部赡軙艽?#xff0c;與為特定任務(wù)而專門編寫的代碼相比,可能運行速度也要慢些。并且,基于 Windows 的控件結(jié)構(gòu)如 Windows 窗體控件和早期的 ActiveX、OCX 和 VBX 控件,僅可以在 Windows 環(huán)境中運行 - 在當今幾乎每天都有新的平臺類型脫穎而出的異類 Internet 環(huán)境中,這很成問題。
為了能夠跨平臺運行,Microsoft 設(shè)計了Web 窗體結(jié)構(gòu) ASP.NET。使用 Visual Studio,您可以從工具箱中選擇稱為 Web 窗體控件的組件,并將它們放置在 ASPX 頁上。然后,您可以設(shè)置該控件的屬性,并使用任何支持 .NET 的語言編寫代碼,從而將其行為與其他控件相關(guān)聯(lián)。這個過程被設(shè)計為與用 Visual Basic 編寫應(yīng)用程序類似,對于大多數(shù)程序員來說,這都是一個非常熟悉的過程。
圖 1 頁請求
客戶端請求包含 Web 窗體控件的頁時,ASP.NET 處理器將加載該頁并在服務(wù)器上創(chuàng)建這些控件,如圖 1 所示,然后執(zhí)行該頁的編程邏輯,將控件關(guān)聯(lián)到一起。該過程結(jié)束時,每個控件都會向 ASP.NET 提供描述其當前外觀的 HTML。這些 HTML 將被返回到客戶端,并在瀏覽器中呈現(xiàn)。請在以下位置閱讀有關(guān) ASP.NET Web 窗體的信息:ASP .NET:Web Forms Let You Drag and Drop Your Way to Powerful Web Apps。
Visual Studio 附帶了一組通用的 Web 窗體控件,或多或少地與 Windows窗體中的可用控件組相對應(yīng)。它包含標簽和文本框等已為大家所熟悉的常用控件,還包含更新、更為復(fù)雜的控件如 DataGrid。
本文介紹了 .NET 框架為編寫自己的 Web 窗體控件而提供的功能。由于我已經(jīng)在 MSDN? Magazine 的 2002 年 4 月刊中介紹了控件的基本概念(方法、屬性和事件),本文中,我將主要介紹 Web 窗體控件與基于 Windows 的控件之間的區(qū)別。之所以存在這些區(qū)別,主要是由于在相對簡陋的瀏覽器運行庫環(huán)境中運行,而不是在資源豐富的Windows 環(huán)境中運行。
.NET 對開發(fā) Web 窗體控件的支持
.NET 框架中包含預(yù)先創(chuàng)建的軟件類,這些類使得編寫 Web 窗體控件變得相對簡單。您需要理解 HTML 才可以生成控件所要求的輸出,就像 Windows 窗體控件的設(shè)計者需要理解 Windows GDI 一樣。但是,掛鉤到 ASP.NET 頁生存周期的例程、在多次調(diào)用之間維護狀態(tài)的例程和檢測宿主瀏覽器功能的例程(全部控件都具備的基礎(chǔ)結(jié)構(gòu))已經(jīng)為您編寫好了。
您可以使用自己所選擇的與 .NET 兼容的語言來編寫控件,通過從自己選擇的若干個 .NET 框架類繼承來使用預(yù)先創(chuàng)建的基礎(chǔ)結(jié)構(gòu)。這些基類大致對應(yīng)于 Windows 窗體中的類。但是,常見的情況是,Microsoft 使用了同一個名稱來在看上去類似的環(huán)境中指代不同的事物,因此,必須謹慎從事,并閱讀附屬細則。下面介紹開發(fā) Web 窗體控件的五種方法。
第一種選擇是從 System.Web.UI.Control 派生,該類是所有 Web 窗體控件的基類。它參與 ASP.NET 頁呈現(xiàn)過程的所有生存周期事件。文檔中指出該類沒有任何特定于用戶界面的功能,雖然它存在于 System.Web.UI 命名空間中。我有不同意見。它包含一個 Render 方法(將在下一節(jié)對其進行介紹),控件使用該方法來發(fā)出要在瀏覽器中顯示的 HTML。它比我要介紹的下一個類具有更少的內(nèi)置 UI 屬性;例如,它沒有 Width、Height、ForeColor、BackColor 和 Font,但如果您一定要使用它來生成 UI,實現(xiàn)起來仍是非常容易的。由于省略了以上元素,它更為輕型化,但由此所造成的差異微乎其微。如果您不關(guān)注它所省略的任何功能,使用它作為基類完全沒有問題。
您的下一個選項是從 System.Web.UI.WebControls.WebControl 派生,該類派生自 System.Web.UI.Control。這是一個添加了基本用戶界面屬性的控件。當您生成新的 Web 控件庫項目時,Visual Studio 會自動使用它作為基類。如果您想要得到像大多數(shù)控件一樣提供用戶界面的控件,您可能應(yīng)當從這里開始。
您還可以從現(xiàn)有的 Web 窗體控件入手,可以是 Visual Studio 附帶的控件,或者從第三方購買的控件。此時,您需要使用 .NET 繼承機制從現(xiàn)有控件派生自己的控件。您需要重用現(xiàn)有功能中自己需要的部分,重寫要更改的部分,并添加要使控件具備的任何附加功能。在我以前的文章中,曾經(jīng)介紹了如何使用 Windows 窗體文本框控件來完成以上任務(wù)。針對 Web 窗體控件完成該任務(wù)與此完全相同,這里不再重復(fù)介紹。
另一個選項是設(shè)計一個包含其他控件的控件。在 Windows 窗體中,它稱為用戶控件,但是在 Web 窗體中,它稱為復(fù)合控件。您可以為自己的基類選擇以上所介紹的三種繼承方案中的任一種。不幸的是,Web 窗體不像 Windows 窗體一樣支持添加子控件。因此,必須在代碼中手動創(chuàng)建和定位子控件。這并不困難,因此這里不作討論。但是,我發(fā)現(xiàn)這一省略頗為令人吃驚。
最后,您可以選擇創(chuàng)建用戶控件。以上所討論的每個控件都是完全編譯的 .NET 程序集。它們可用于 Visual Studio 工具箱和設(shè)計器,并可以保存在全局程序集緩存 (GAC) 中,因此無需為每個要使用它們的客戶端使用一個單獨的副本。Web 窗體提供了另外一種生成可重用控件包的方法(用戶控件)。像您在 Windows 窗體中看到的用戶控件一樣,Web 窗體用戶控件是通過在 Visual Studio 設(shè)計器中將其他控件放置到設(shè)計圖面上來生成的。但是,與 Windows 窗體用戶控件不同,這種用戶控件是一個 HTML 頁,而不是已編譯的程序集,因此不能保存到 GAC 或 Visual Studio 工具箱中,而且,也不能在 Visual Studio 設(shè)計器中顯示其外觀。因此,我認為它的有用性遠不如我所介紹的其他類型的 Web 窗體控件。我懷疑它之所以存在,是因為復(fù)合 Web 窗體控件中缺少設(shè)計器支持。
返回頁首簡單的自定義控件
在學(xué)習(xí)或者講解新軟件時,我總會從我所能想到的最簡單的示例入手。這是一個標簽控件,它包含一個稱為 Text 的屬性,該屬性是標簽所顯示的字符串,它還包含一個稱為 ForeColor 的屬性,即該文本字符串的顏色(參見圖 2)。
圖 2 標簽控件
我首先在 Visual Studio 中生成了該項目,然后從 New Project 對話框中選擇了 Web Control Library,如圖 3 所示。向?qū)闪艘粋€項目,該項目包含一個派生自 System.Web.UI.WebControls.WebControl 的新類。向該類中添加方法和屬性與向任何其他 .NET 類添加方法和屬性完全相同。實際上,系統(tǒng)提供的基類已經(jīng)包含了本示例中所使用的稱為 Text 和 ForeColor 的屬性。
圖 3 生成項目
就像我說的一樣,這非常簡單?;旧鲜沁@樣的。理解自定義 Web 窗體控件的關(guān)鍵在于 Render 方法,該方法在概念上與 Windows 窗體 OnPaint 方法相同,區(qū)別僅在于前者發(fā)出 HTML,而后者則發(fā)出 GDI 調(diào)用。當 ASP.NET 服務(wù)器框架為響應(yīng)用戶請求而匯編 Web 窗體頁時,它將創(chuàng)建頁上列出的控件,設(shè)置這些控件的屬性和持久性數(shù)據(jù),然后調(diào)用它們的各個 Render 方法??蚣軐嶋H上是告訴控件:“你是活動的,并正處于預(yù)期的狀態(tài)。你需要告訴我你的外觀,因為我沒有別的辦法來了解?!盬eb 窗體控件的作者會在 Render 方法中放入一些代碼,以發(fā)出 HTML,告知瀏覽器如何根據(jù)控件的當前狀態(tài)和屬性,以及與控件有關(guān)的環(huán)境中的任何其他信息來顯示控件的外觀。
Web 窗體控件的作者需要了解 HTML,這是因為環(huán)境提供的相關(guān)摘要信息很少。以下是指定文本顏色的 HTML:
<span style="color:green;"> Here is some text </span>
為了生成這些 HTML,我編寫了圖 4 中所示的代碼。大多數(shù)讀者都告訴我他們希望源代碼使用 Visual Basic 來編寫;為了照顧 C# 讀者,我使用 Visual Basic 和 C# 兩種語言編寫了可下載的示例代碼。
當 ASP.NET 框架調(diào)用控件的 Render 方法時,將傳遞一個 System.Web.UI.HtmlTextWriter 類型的對象。這在概念上類似于 OnPaint 方法在 Windows 窗體控件中收到的 System.Windows.Forms.PaintEventArgs 的 Graphics 成員。兩者都代表到框架的連接,該框架將輸出定位到其相應(yīng)的位置。HtmlTextWriter 包含的方法、屬性和常數(shù)使得您的控件能夠?qū)?HTML 發(fā)送到將被發(fā)送到客戶端瀏覽器的輸出頁上。在示例代碼中,我首先調(diào)用了方法 AddStyleAttribute,該方法在內(nèi)部創(chuàng)建一個稱為 style 的 HTML 屬性,將其值設(shè)置為控件所繼承的 ForeColor 屬性的值,然后將其添加到內(nèi)部緩沖區(qū)??梢酝ㄟ^對 AddStyleAttribute 方法的附加調(diào)用向緩沖區(qū)添加 style 屬性的附加值;通過調(diào)用 AddAttribute 方法,可以添加其他屬性,當然,在本例中,并不需要這么做。
接著,我調(diào)用了方法 RenderBeginTag,指定文本中要顯示的 HTML 標記的名稱,本例中為“span”。該調(diào)用從內(nèi)部緩沖區(qū)中提取任何屬性(此處為 style),將它們放置到標記中,然后寫入 HTML輸出流中。這兩個調(diào)用生成了第一行 HTML:
<span style="color:green;">
接下來,為了編寫標簽的文本,我調(diào)用了方法 HtmlTextWriter.Write,以傳遞控件的內(nèi)部文本字符串。該方法將文本字符串 verbatim 傳遞到 HTML 輸出流中,從而生成了第二行:
Here is some text
為了關(guān)閉 標記,我調(diào)用了 HtmlTextWriter.RenderEndTag。這導(dǎo)致編寫器讀回最后一個打開的標記,并發(fā)出該標記的關(guān)閉標記,在本例中為 ,以作為最后一行 HTML。
該對象包含用于執(zhí)行輸出的其他方法,這些方法能夠提供更為精細的控件,但較為復(fù)雜。為簡單起見,我將在本文的其余部分中沿用前面的方法。
圖 5 向工具箱中添加新的控件
最后,我需要一個客戶端來使用該控件,以便對其進行調(diào)試和顯示。我向現(xiàn)有的解決方案添加了一個包含 ASP.NET 頁的新 Web 應(yīng)用程序。為了向工具箱中添加新的 Web 窗體控件,我右鍵單擊并選擇了 Customize Toolbox,調(diào)出了圖 5 中所示的對話框。我瀏覽到了新的 Web 控件 DLL,選擇了它,該控件即出現(xiàn)在控件列表中,如圖 6 中所示。
圖 6 控件列表中的新控件
之后,就可以將它放置到 ASPX 頁上并設(shè)置其屬性。生成項目并在瀏覽器中啟動它時,控件的外觀如圖 2 所示。
返回頁首更為復(fù)雜的示例
現(xiàn)在,我們已經(jīng)介紹了 .NET Web 控件的基本功能,接下來,讓我們看一個示例,該示例演示了 Web 窗體控件如何為頁上的其他控件激發(fā) .NET 事件。我發(fā)現(xiàn) SDK 文檔在介紹 Web 控件事件時很模糊。它將用戶在瀏覽器上的單擊(從而啟動過程)、對它所觸發(fā)的服務(wù)器的回發(fā),以及收到回發(fā)的服務(wù)器端控件向頁上的其他控件發(fā)送的通知全部都定義為事件。下面,我將嘗試對這些事件之間的區(qū)別進行準確的說明。
我編寫了一個更為復(fù)雜的示例控件,該控件顯示在圖 7 中的 ASPX 頁上。它在用戶的瀏覽器中顯示一個表格,該表格的每個單元格中都顯示自己的行號和列號。該控件公開稱為 Rows 和 Columns 的屬性。這兩個屬性都是整型數(shù),在設(shè)計時設(shè)置。當用戶單擊表格中的任何單元格時,窗體將被回發(fā)到服務(wù)器。在服務(wù)器上匯編并初始化該頁后,該表格控件將確定用戶單擊的是哪個單元格,并在服務(wù)器上向頁上任何愿意偵聽的其他控件激發(fā)一個 .NET 框架事件。在本例中,頁中包含一個事件處理程序,該處理程序設(shè)置一個單獨的標簽控件的值,以顯示用戶單擊的單元格的行號和列號。
圖 7 表格控件
開發(fā)此控件時,我先是將 Render 方法編寫為直接發(fā)出顯示具有所需行數(shù)和列數(shù)的表格的 HTML,那非常簡單。接著,我想要添加導(dǎo)致瀏覽器在用戶單擊表格中的單元格時將窗體回發(fā)到服務(wù)器的 HTML。這需要使用一些其他技巧(參見圖 8)。
可以看到,每個表格數(shù)據(jù)項 () 都創(chuàng)建一個單元格,且都包含一個 onClick 屬性,該屬性調(diào)用一個客戶端腳本,傳遞要處理回發(fā)的服務(wù)器端控件(本例中為表格控件)的 ID 以及一個包含任意參數(shù)的字符串。這里,該字符串為表格單元格的文本,這使得服務(wù)器端控件能夠標識用戶所單擊的單元格,但該字符串可以是您所需要的任何內(nèi)容。圖片底部顯示的客戶端回發(fā)腳本將這些參數(shù)放置到隱藏的輸入控件中,并執(zhí)行對服務(wù)器的回發(fā)。
稍后我將講解對這種回發(fā)的服務(wù)器端處理,讓我們首先看看這些 HTML 是如何生成的。這看上去很繁瑣,好消息是您無需自己編寫它們。請記住,您的控件存在于 ASP.NET 頁上,因此能夠通過基礎(chǔ) Page 類成員變量訪問該頁的所有方法。方法 Page.GetPostBackEventReference 導(dǎo)致框架生成頁上的 HTML 腳本,并返回調(diào)用它的 HTML 字符串。接著,我將該字符串添加到 元素的屬性中,該元素能夠使用前述的 HtmlTextWriter 方法。您可以在圖 9 中看到 Render 方法的代碼。請暫且忽略處理視圖狀態(tài)的代碼部分;我將在下一節(jié)中對它們進行說明。)
如果瀏覽器不能運行 JScript 怎么辦?我將在稍后進行講解。現(xiàn)在,先假定瀏覽器能夠運行 JScript,或者,我們明確聲明我們僅關(guān)注瀏覽器能夠運行 JScript 的情況。
回發(fā)窗體到達 ASP.NET,ASP.NET 在服務(wù)器上加載目標頁,并創(chuàng)建它上面的控件。ASP.NET 需要將回發(fā)傳遞給它的目標控件。ASP.NET 是通過客戶端腳本填充的隱藏輸入控件得知該目標控件的。服務(wù)器端控件通過實現(xiàn)稱為 IPostBackEventHandler 的接口并重寫 RaisePostBackEvent 方法來接受該輸入通知。我認為該方法名稱非常容易引起誤解。它不會引發(fā)回發(fā)事件;它通過 ASP.NET 從瀏覽器接受一個事件并引發(fā)一個服務(wù)器端 .NET 事件。該方法存在的唯一目的是將瀏覽器發(fā)送的通用窗體回發(fā)事件轉(zhuǎn)換為具有有用參數(shù)的已命名且有意義的 .NET 控件事件,其他服務(wù)器端控件可以偵聽該事件,頁設(shè)計器可以方便地為該事件編寫代碼。如果將它看作 AcceptPostbackAndOptionallyRaiseServerEvent,那么您大腦中得到的就是正確的模型。
我在這個命名方式很差勁的方法中放入了一些代碼,在控件從用戶的瀏覽器接收回發(fā)時執(zhí)行這些代碼(參見圖 10)。查明哪個控件應(yīng)當接收回發(fā)后,ASP.NET 調(diào)用此方法并向它傳遞 eventArgument 字符串,該字符串是由客戶端傳遞給客戶端腳本然后在隱藏輸入變量中傳輸?shù)摹1纠?#xff0c;該字符串為用戶單擊的表格單元格的文本。我的示例代碼可以從字符串中分析出行號和列號,因此服務(wù)器端處理程序知道用戶單擊了哪個單元格。
如果已知您的控件是唯一一個關(guān)注誰導(dǎo)致了回發(fā)的控件,什么也不需要做;只需在 RaisePostBackEvent 方法中的適當位置編寫處理程序代碼即可。但是,您的控件在收到回發(fā)后所需要完成的主要任務(wù)之一是通知頁上的其他控件您的控件上發(fā)生了某些事件。
為此,您的控件需要使用 Windows 窗體控件中使用的相同通用事件處理機制向頁和其他服務(wù)器控件激發(fā) .NET 事件。本例中,我向控件添加了一個稱為 TableCellClicked 的事件,其中包含兩個參數(shù),即單擊的行和列,如圖 10 所示??梢詾樗璧娜魏慰丶惭b處理程序,以接收該事件。在我的示例中,頁中包含一個處理程序,接收來自表格控件的事件,并將所單擊的單元格設(shè)置到標簽控件中(參見圖 11)。
總之,Web 窗體控件中的事件處理包括兩個必需的部分,和一個可選的第三部分。首先,控件的 Render 方法必須生成客戶端 HTML,這些 HTML 在客戶端發(fā)生您所關(guān)注的事件時導(dǎo)致對控件的回發(fā)。其次,您的控件必須實現(xiàn) IPostBackEventHandler 接口,以便 ASP.NET 能夠通知您的控件它收到了該回發(fā),并傳遞有關(guān)它的附加信息。接下來是一個可選的部分,您的控件可以(很可能會選擇)激發(fā) .NET 事件,這樣其他控件就可以接收所發(fā)生的這些事件的通知。
返回頁首視圖狀態(tài)管理
Web 頁本質(zhì)上是無狀態(tài)的。這么講的意思是一個頁上所顯示的內(nèi)容與用戶以前所查看的內(nèi)容無關(guān),除非編寫代碼將它們關(guān)聯(lián)起來。當用戶只是查看靜態(tài)文本頁時,沒有太大問題。但是,由于目前與 Web 站點的大多數(shù)交互都涉及跨越多個頁的持續(xù)會話,這將造成嚴重的問題。SDK 文檔聲稱您必須“向用戶提供連續(xù)性的假相”。文檔作者對問題的分析是完全過時的。用戶體驗是一切的核心,這是金科玉律。應(yīng)當是通過您的代碼來達到用戶的預(yù)期,而不是其他方式。如果您的編程模型與用戶的需求不符,您就必須編寫代碼來使其相符。用戶的連續(xù)性是真實的,真正的假相是代碼的連續(xù)性。
在 ASPX 頁上編寫代碼的設(shè)計者能夠訪問 Session 和 Application 集合對象等功能,以維護一個頁到另一個頁的狀態(tài)。但是控件設(shè)計者無法使用它們,因為她不知道頁的會話狀態(tài)何時因超時而被放棄,或者被頁程序員顯式轉(zhuǎn)儲。實際上,她甚至不知道是否已打開會話狀態(tài),并且無法在未打開會話狀態(tài)時對其進行更改。因此,如果您的控件需要在對頁的一次呈現(xiàn)到另一次呈現(xiàn)之間維護狀態(tài),需要采用其他方法。
.NET 框架提供了一種機制,使得 Web 窗體控件能夠安全且方便地維護它們的狀態(tài)??丶惏粋€稱為 ViewState 的成員。它是一個與 Session 和 Application 對象集合使用的屬性包集合類型相同的屬性包集合,不同之處在于它將自己的數(shù)據(jù)存儲在頁上的一個隱藏文本字段中。您可以在圖8 中名為 __VIEWSTATE 的隱藏輸入字段中看到視圖狀態(tài)。
當您的控件將數(shù)據(jù)置入視圖狀態(tài)集合中時,ASP.NET 將它序列化到 ViewState 字符串中,并將其作為已呈現(xiàn)的頁的一部分傳輸?shù)娇蛻舳?。當頁被回發(fā)到服務(wù)器時,ASP.NET 將提取隱藏的變量并將其反序列化到各個控件的 ViewState 成員變量。這種體系結(jié)構(gòu)對于使用網(wǎng)絡(luò)場時的可伸縮性尤其有利,這是因為它避免了任何類型的服務(wù)器關(guān)系。任何處理回發(fā)的計算機都可以看到上一次的狀態(tài),并為下一次存儲它(可能是更新后的狀態(tài))。
我將我的示例表格控件編寫為使用 ViewState 記憶其單擊狀態(tài),即用戶單擊的單元格的行和列。在我的控件的構(gòu)造函數(shù)中,可以看到我創(chuàng)建了用于記憶所單擊的行和列的視圖狀態(tài)變量,并將它們設(shè)置為 -1,指示無選項。
' Start member variables in desired default state;
' no selected cellPublic Sub New()Me.ViewState("SelectedRow") = -1Me.ViewState("SelectedColumn") = -1End Sub
從客戶端收到單擊回發(fā)時(參見圖 10),我提取了用戶所選擇的單元格并將其存儲到 ViewState 中。呈現(xiàn)時(參見圖 9),我提取了選定的行和列,并調(diào)整 HTML 以正確地顯示選定的單元格。這就是所有的步驟。很簡單是吧?
請注意,基類包含一個稱為 EnableViewState 的成員變量,它的說明指出它通知控件是否將其內(nèi)部狀態(tài)保存到 ViewState 中。但是,如果使用示例代碼,將發(fā)現(xiàn)該變量看上去對其無任何效果。這是因為該變量沒有在內(nèi)部關(guān)閉 ViewState 機制。它只是一個 Boolean 標志,用于通知控件:對于頁設(shè)計器來說,最好提前關(guān)閉 ViewState。是否編寫代碼以檢查并響應(yīng)該變量的狀態(tài)全在乎您的選擇,在本例中,我選擇了這么做??吹接腥诉`反了最少驚詫原則時多么惹人生厭了嗎?所以千萬別對自己的客戶這么做,好嗎?
返回頁首客戶端腳本
前面講解過的大多數(shù)控件功能都發(fā)生在服務(wù)器端,事實上,許多人將這些 Web 窗體控件稱為“服務(wù)器控件”,以便強調(diào)這一點。真正好的 Web UI 設(shè)計通常要求客戶端上至少存在一些瀏覽器腳本形式的代碼。例如,Web 窗體工具包中的驗證程序控件確保用戶使用滿足其條件的數(shù)據(jù)(任何字符串、有效的電子郵件地址、5 到 15 之間的整數(shù)等)填充了表單中必需的字段,然后才允許提交窗體。如果數(shù)據(jù)未能滿足驗證程序的條件,該驗證程序?qū)@示一條錯誤信息,并中止回發(fā)。這節(jié)省了網(wǎng)絡(luò)帶寬、服務(wù)器周期,并可以防止用戶產(chǎn)生沮喪情緒,這是因為反饋是即時的。這正是我們所希望看到的組合。
ASP.NET 框架提供了用于 Web 窗體控件的內(nèi)置功能,使其能夠方便地發(fā)出要放在返回到客戶端的頁中的腳本,并能夠方便地訪問它們自身或其他控件放置在該頁中的腳本。我編寫了一個示例程序,顯示了控件可以在返回給客戶端的頁上放置腳本的三個位置(參見圖 12、圖 13 和圖 14)。
圖 12 為示例應(yīng)用程序編寫腳本
控件用于在頁上放置腳本的所有方法都位于 Page 成員變量中,該變量表示控件所在的頁。控件可以通過 Page.RegisterStartupScript 方法在頁上放置一個啟動腳本。在用戶的瀏覽器中顯示該頁時,該腳本將自動執(zhí)行。該示例程序在顯示頁時僅彈出一個消息框。您還可以使用 Page.RegisterClientScriptBlock 方法放置一個腳本塊,該腳本塊需要由頁上的其他腳本代碼顯式調(diào)用。示例程序在您單擊頁上的文本時僅會彈出一個消息框。
兩個方法都接受兩個字符串參數(shù):腳本塊和腳本本身的名稱。如果兩個控件試圖使用相同的名稱注冊一個腳本,頁將忽略第二次嘗試。這意味著要避免與不是由您自己編寫的其他控件發(fā)生命名沖突,行業(yè)級的控件應(yīng)當使用唯一的長名稱,而避免使用一般性的短名稱,如 MyScript。如果要在注冊腳本塊之前查明該腳本塊是否已被注冊,可以使用 Page 對象上的兩個方法來實現(xiàn),即 IsClientScriptBlockRegistered 和 IsStartupScriptBlockRegistered。
您所傳遞的腳本可以是文字的,如本示例中的腳本。但是,有時腳本可能會很長,像驗證程序一樣。這種情況下,您可能需要將腳本放置到單獨的文件中,以便在運行時提取。本例中,可以將腳本標記編寫為使用 src 屬性將腳本執(zhí)行引擎指向腳本文件的位置,如下所示:
<script language="javascript" src="/aspnet_client/system_web/1_0_3328_4/WebUIValidation.js"> </script>
客戶端腳本最常見的用途可能是在允許提交窗體之前對數(shù)據(jù)進行驗證。為此,必須通知您的代碼即將發(fā)生的回發(fā)操作,并使腳本具有在要求未能得到滿足時取消回發(fā)的能力。使用 ASP.NET 框架,可以通過 Page.RegisterOnSubmitStatement 方法注冊提交處理程序。提交窗體時,瀏覽器將逐步通過所有已注冊的提交語句,以查看是否應(yīng)當繼續(xù)提交。您的提交語句必須包含您的驗證邏輯,如果要允許提交操作繼續(xù),則返回 true,否則返回 false。示例程序?qū)⒁粋€提交處理程序放入生成的腳本中,該腳本在單擊 Submit 按鈕時彈出一個提示框。如果輸入“Y”,提交將繼續(xù);否則將中止操作。
并不是所有的瀏覽器都可以運行以您首選的語言編寫的腳本,有些瀏覽器根本不能運行腳本。您需要一種檢測這種情況的方法,這樣您的控件就可以決定是要以降級方式運行,通知用戶,還是根本不運行。例如,當驗證程序控件無法在客戶端執(zhí)行其驗證任務(wù)時,將在服務(wù)器端運行其邏輯。實際上,即使它們認為自己已經(jīng)成功地在客戶端運行,它們?nèi)詴诜?wù)器端運行,這只是為了確保它們沒有以某種方式錯誤地被中止或者篡改,從而將無效的數(shù)據(jù)注入服務(wù)器。
Page.Request.Browser 對象保存有關(guān)瀏覽器功能的信息。示例代碼讀取屬性 Type、VBScript、JavaScript 和 EcmaScriptVersion,并將它們顯示在頁上供您查看。您可能還希望控件上具有一個允許頁設(shè)計器關(guān)閉腳本功能(即使瀏覽器能夠運行腳本)的屬性。驗證程序控件包含一個稱為 EnableClientScript 的 Boolean 屬性。您可能需要遵照這一設(shè)計模式,除非您的控件在沒有客戶端腳本功能的情況下毫無意義。
返回頁首結(jié)論
Web 窗體簡化了編寫優(yōu)秀 Web 應(yīng)用程序的過程,而 .NET 框架則使您能夠輕松地創(chuàng)建 Web 窗體控件。您所必須理解的只是控件的業(yè)務(wù)邏輯,以及如何以 HTML 呈現(xiàn)它;基礎(chǔ)結(jié)構(gòu)的其余部分都是由從 .NET 框架繼承的類處理的。
相關(guān)文章,請參閱:
ASP .NET: Web Forms Let You Drag and Drop Your Way to Powerful Web Apps
Windows Forms: Developing Compelling User Controls that Target Forms in the .NET Framework
David S. Platt 是 Rolling Thunder Computing Inc. 的總裁和創(chuàng)始人。他在哈佛大學(xué)和全球各地的公司講授 .NET 技術(shù)。他發(fā)布了一個有關(guān) .NET 的免費電子郵件新聞稿,網(wǎng)址為 http://www.rollthunder.com。David 是 Introducing Microsoft .NET (Microsoft Press, 2001) 一書的作者。
本文摘自 MSDN Magazine 的 2002 年 6 月刊。
總結(jié)
以上是生活随笔為你收集整理的使用 .NET 框架轻松开发完美的 Web 窗体控件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何快速实现HTML编辑器.NET组件
- 下一篇: 使用 ASP.NET 加密口令