飛艷小屋

          程序--人生--哲學(xué)___________________歡迎艷兒的加入

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            52 Posts :: 175 Stories :: 107 Comments :: 0 Trackbacks
          使用客戶端腳本
          使用客戶端腳本
          發(fā)布日期: 9/20/2004 | 更新日期: 9/20/2004

          Scott Mitchell

          4GuysFromRolla.com

          摘要:盡管 ASP.NET 在服務(wù)器上執(zhí)行其大多數(shù)操作,但是某些操作在客戶端進行處理可能會更好。Scott Mitchell 說明了 ASP.NET 頁面和控件如何添加客戶端代碼。

          *
          本頁內(nèi)容 簡介 簡介 創(chuàng)建基類作為添加客戶端腳本的基礎(chǔ) 創(chuàng)建基類作為添加客戶端腳本的基礎(chǔ) 從代碼隱藏類添加客戶端腳本 從代碼隱藏類添加客戶端腳本 根據(jù)對用戶操作的響應(yīng)執(zhí)行客戶端代碼 根據(jù)對用戶操作的響應(yīng)執(zhí)行客戶端代碼 實現(xiàn)常用客戶端功能 實現(xiàn)常用客戶端功能 小結(jié) 小結(jié) 相關(guān)書籍 相關(guān)書籍
          簡介

          當(dāng)使用動態(tài)的、基于 Web 的腳本技術(shù)時,與傳統(tǒng) ASP 或 PHP 類似,開發(fā)人員必須對客戶端和服務(wù)器間的邏輯、暫時和物理分隔有著敏銳的理解。例如,對于觸發(fā)服務(wù)器端代碼執(zhí)行的用戶操作,使用傳統(tǒng) ASP 的開發(fā)人員必須明確地使用戶的瀏覽器將請求返回到 Web 服務(wù)器。創(chuàng)建這樣的交互可能會輕易地占用大量開發(fā)時間,并且導(dǎo)致不易讀的代碼。

          Microsoft ASP.NET 通過使用 Web 窗體,有助于減輕將用戶事件綁定到特定服務(wù)器端代碼執(zhí)行的負(fù)擔(dān),這就模糊了客戶端和服務(wù)器間的界線。使用 ASP.NET 和最少的工作,開發(fā)人員就可以快速地創(chuàng)建如下的網(wǎng)頁,它具有大量的交互式用戶界面元素按鈕、下拉列表等,而這些都基于最終用戶的操作,可以選擇性地運行服務(wù)器端代碼。例如,利用 ASP.NET 添加下拉列表,只要選定的下拉列表項目更改則執(zhí)行某些操作,您只需添加 DropDownList Web 控件、將其 AutoPostBack 屬性設(shè)置為 True,然后為該下拉列表創(chuàng)建一個 SelectedIndexChanged 事件處理程序。如果利用傳統(tǒng)的 ASP 完成上述任務(wù),則會要求編寫許多復(fù)雜的 HTML、客戶端 JavaScript 和服務(wù)器端腳本代碼;利用 ASP.NET,則為您提供了必要的腳本代碼和服務(wù)器端事件模型。

          盡管在執(zhí)行客戶端操作時,ASP.NET 中的 Web 窗體極大地簡化了運行服務(wù)器端腳本,但是,如果誤用這種功能可能會導(dǎo)致無法接受的性能。盡管 Web 窗體隱藏了所涉及的復(fù)雜性,每次需要執(zhí)行服務(wù)器端代碼時,最終用戶的瀏覽器必須通過重新提交窗體,將請求返回到 Web 服務(wù)器。當(dāng)提交窗體時,所有窗體字段(文本框、下拉列表和復(fù)選框等)必須同時返回它們的值。此外,頁面的視圖狀態(tài)也被返回到 Web 服務(wù)器。總而言之,每次回發(fā)網(wǎng)頁時,幾千字節(jié)的數(shù)據(jù)將需要潛在地發(fā)送回 Web 服務(wù)器。于是,經(jīng)常回發(fā)可能很快就會導(dǎo)致 Web 應(yīng)用程序不可使用,尤其是對于那些仍然使用撥號連接的用戶。通過將功能推到客戶端可以降低經(jīng)常回發(fā)的需要。

          ASP.NET Web 窗體發(fā)出一個標(biāo)題為 VIEWSTATE 的隱藏窗體字段,它包含 Web 窗體中 Web 控件已更改狀態(tài)的基于 64 位編碼的表示。根據(jù)出現(xiàn)的 Web 控件,視圖狀態(tài)的范圍可以從幾十字節(jié)到幾萬字節(jié)。要學(xué)習(xí)有關(guān)視圖狀態(tài)的更多知識,請查閱我的文章 Understanding ASP.NET View State

          利用傳統(tǒng)的 ASP,添加數(shù)據(jù)驅(qū)動、自定義客戶端腳本非常簡單,但并不是非常易讀。例如,要在傳統(tǒng)的 ASP 中顯示根據(jù)某個 ID 字段加載 URL 的彈出窗口,您可以使用 語法來插入 ID 字段的值,在適當(dāng)?shù)目蛻舳四_本中進行輸入。ASP.NET 允許您利用 Page 類中的各種方法,創(chuàng)建這種數(shù)據(jù)驅(qū)動的客戶端腳本。

          本文分析了向 ASP.NET 網(wǎng)頁添加客戶端腳本的技術(shù)。客戶端腳本是運行在訪問者瀏覽器中的腳本代碼,如其名字所示。我們將看到如何完成常見的客戶端任務(wù),例如顯示警告、確認(rèn)框和彈出窗口。(客戶端腳本窗體字段驗證的一個主要用途可能與 ASP.NET 主題有點不相關(guān),因為驗證程序 Web 控件提供了隨取隨用的客戶端窗體驗證。)本文的重點在于插入客戶端腳本的服務(wù)器端類、方法和技術(shù);我們不會詳細(xì)地分析實際的客戶端腳本,因為該信息涉及了圍繞 Web 的眾多其他文章和站點。

          創(chuàng)建基類作為添加客戶端腳本的基礎(chǔ)

          傳統(tǒng)的 ASP 和 ASP.NET 之間的主要差別之一在于各自技術(shù)的編程模型。ASP 頁面是原子的、程序上的腳本,解釋每個頁面的訪問。然而,ASP.NET 完全是面向?qū)ο蟮木幊碳夹g(shù)。所有 ASP.NET 網(wǎng)頁都是帶有屬性、方法和事件的類。所有網(wǎng)頁直接或間接地派生自 System.Web.UI 命名空間中的 Page 類,Page 類包含了 ASP.NET 網(wǎng)頁的基本功能。

          面向?qū)ο缶幊痰母拍钪痪褪抢^承。繼承使您可以創(chuàng)建一個擴展其他類功能的新類。(如果類 B 繼承類 A,也可以說擴展了 A;類 A 被稱為基類。)當(dāng)使用代碼隱藏模型來創(chuàng)建 ASP.NET 網(wǎng)頁時,可以非常清楚地看到代碼隱藏類繼承了 Page 類:

          Public Class WebForm1 Inherits System.Web.UI.Page ... End Class

          通過使您的代碼隱藏類繼承 Page 類,它自動接收在 Page 類中繼承的功能,例如 RequestResponseSessionViewState 對象以及常見事件,如 InitLoadRender 等等。我們將在本文中看到,如果您需要可用于所有 ASP.NET 網(wǎng)頁的某個常見功能,一種方法是創(chuàng)建派生自 Page 類并具有完成這些所需增強功能的其他方法和屬性的類。然后,要使 ASP.NET 網(wǎng)頁利用這些增強功能,我們只需更新頁面代碼隱藏類中的 Inherits 語句,以使用擴展 Page 類的類。

          在本文中,我們將創(chuàng)建一個名為 ClientSidePage 的類,它派生自 Page 類并提供額外的方法以協(xié)助執(zhí)行常見的客戶端任務(wù)。通過讓代碼隱藏類繼承 ClientSidePage,而不是繼承 Page,添加腳本代碼將會像調(diào)用方法和傳送幾個參數(shù)那樣簡單。具體說來,該類包括用于下列用途的方法:

          ?

          顯示模式客戶端對話框。

          ?

          在頁面加載時將焦點設(shè)置到特定的窗體字段。

          ?

          使用模式確定對話框來確定用戶是否希望回發(fā)該窗體。

          ?

          顯示彈出窗口。

          在我們深入研究 ClientSidePage 類之前,首先讓我們分析一下 Page 類中的有關(guān)方法,以便將客戶端腳本插入到網(wǎng)頁中。在我們討論這些 Page 方法后,我們將開始利用 ClientSidePage 類擴展它們的功能,并且查看如何將所有內(nèi)容結(jié)合在一起以及如何在 ASP.NET 網(wǎng)頁中使用擴展的類。

          從代碼隱藏類添加客戶端腳本

          所有 ASP.NET 網(wǎng)頁必須直接或間接地派生自 System.Web.UI 命名空間中的 Page 類。Page 類包含正常運行的網(wǎng)頁所要求的方法、屬性和事件的基本集合。在該類的眾多方法中,一些方法旨在將客戶端腳本插入到生成的 HTML 中。這些方法從代碼隱藏類調(diào)用,因此可以用于發(fā)出數(shù)據(jù)驅(qū)動的客戶端腳本。用于發(fā)出客戶端腳本的相關(guān) Page 類方法如下所示。

          該基類派生自 System.Web.UI.Page 類,因此可以通過從代碼隱藏類直接調(diào)用 Page 類的公共方法來訪問它們。

          要訪問 Page 類的方法,您可以直接鍵入方法名,或者通過輸入 MyBase.(對于 Microsoft Visual Basic .NET)、this. (對于 C#)或者 Page.(對于 C# 或 Visual Basic .NET),利用 Microsoft Visual Studio .NET 中的 IntelliSense 來實現(xiàn)。如果使用 Visual Basic .NET 作為選擇的編程語言,請確保將 Visual Studio .NET 配置為不 隱藏高級成員,否則將無法看到這些客戶端腳本方法。(要顯示高級成員,請轉(zhuǎn)到 Tools | Options | TextEditor | Basic,然后清除 Hide advanced members 復(fù)選框。)

          RegisterClientScriptBlock(key, script)

          在 Web 窗體已呈現(xiàn)的 <form> 元素之后,在包含于 Web 窗體中的任意 Web 控件之前,RegisterClientScriptBlock 方法會添加一個客戶端腳本塊。key 輸入?yún)?shù)允許您指定與該腳本塊相關(guān)聯(lián)的唯一的密鑰,而 script 參數(shù)包括要發(fā)出的完整的腳本代碼。(這個 script 參數(shù)應(yīng)該包括實際的 <script> 元素,同時還包括客戶端 JavaScript 或 Microsoft VBScript。)

          在通過 ASP.NET 網(wǎng)頁的代碼隱藏類發(fā)出客戶端腳本時,通常情況下,key 參數(shù)的值就不是非常重要了。簡單地選擇一個說明性的密鑰值。在通過自定義編譯的服務(wù)器控件插入客戶端腳本代碼時,key 參數(shù)就更加適用。編譯的控件有可能需要一組客戶端函數(shù)。一個頁面上服務(wù)器控件的多個實例可以共享這些公用客戶端腳本函數(shù),因此對于整個頁面而言,這些函數(shù)只需要發(fā)出一次即可,不需要每個控件實例發(fā)送一次。例如,驗證控件利用客戶端代碼來增強用戶體驗。如果頁面上存在任意驗證控件,這種客戶端代碼就必須存在,但是如果存在多個驗證控件,那么全部控件都可以使用這個單個的共享函數(shù)的集合。

          通過為腳本塊提供一個密鑰,利用公用客戶端函數(shù)集合構(gòu)建控件的控件開發(fā)人員可以檢查所要求的公用函數(shù)集合是否已經(jīng)被頁面上控件的另一個實例添加。如果已添加,它不需要重新添加公用腳本。要檢查腳本塊是否已經(jīng)使用相同的密鑰添加,請使用 IsClientScriptBlockRegistered(key) 方法,它將返回布爾值,表示帶有相同密鑰的腳本塊是否已經(jīng)進行了注冊。需要注意的是可以在不首先檢查它是否注冊的情況下添加腳本塊。如果嘗試?yán)靡呀?jīng)注冊的密鑰添加腳本塊,添加的腳本塊將被忽略,并且原來的腳本將保持分配到該密鑰。

          IsClientScriptBlockRegistered 方法在以下兩種情況下尤為有用。第一,當(dāng)添加相似但又獨特的腳本塊時它很方便,您需要確保每個腳本塊都給予唯一的密鑰。本文稍后分析的代碼說明了“is registered”方法的實用性。第二個用途就是當(dāng)構(gòu)建需要某個公用腳本的控件時,尤其是如果特別的生成該腳本。通過使用 IsClientScriptBlockRegistered 方法,可以確保對頁面上服務(wù)器控件的所有腳本通用的腳本在每次頁面加載時只生成一次,而不是對頁面上的每個控件實例都生成一次。

          RegisterClientScriptBlock 方法對于添加客戶端腳本非常有用,該腳本不依賴于 Web 窗體內(nèi)出現(xiàn)的任意窗體字段。該方法的常見使用就是顯示客戶端警告框。例如,設(shè)想您具有一個帶有一些 TextBox Web 控件和一個“Save”按鈕的網(wǎng)頁。TextBox 控件可能會具有來自數(shù)據(jù)庫的特殊值。設(shè)想該頁面允許用戶修改這些值并通過單擊“Save”按鈕提交他們所做的更改。當(dāng)單擊“Save”時,網(wǎng)頁將會回發(fā),并且會觸發(fā)按鈕的 Click 事件。您可以為更新數(shù)據(jù)庫的事件創(chuàng)建一個服務(wù)器端事件處理程序。要使用戶知道他們的更改已經(jīng)保存,您可能希望顯示一個警告框“Your changes have been saved”。通過將以下代碼行添加到按鈕的 Click 事件處理程序中,可以實現(xiàn)這個任務(wù):

          RegisterClientScriptBlock("showSaveMessage", _ "<script language=""JavaScript""> _ alert('Your changes have been saved.'); _ </script>")

          上述代碼將會在頁面的 <form> 內(nèi)添加指定的腳本內(nèi)容,但是在該窗體的內(nèi)容前。當(dāng)在用戶瀏覽器中生成頁面時,他們將會看到根據(jù)頁面加載顯示的客戶端警告框,如圖 1 所示。

          <form method="post" ...> <script language="JavaScript"> alert('Your changes have been saved.'); </script> ... </form>

          1. 客戶端 JavaScript 的結(jié)果

          上面示例中一個潛在地不需要的副作用就是,警告框?qū)跒g覽器接收到 <form> 標(biāo)記后立即顯示。在用戶單擊警告框的“OK”按鈕之前,瀏覽器將掛起網(wǎng)頁的呈現(xiàn)。這意味著用戶在單擊“OK”之前,將看到一個空白的瀏覽器頁面。如果希望在顯示警告框之前完全顯示該頁面,您可以使用 RegisterStartupScript 方法(我們將在下面進行討論),在 <form> 元素的結(jié)尾處插入 JavaScript。

          RegisterStartupScript(key, script)

          RegisterStartupScript 方法與 RegisterClientScriptBlock 方法非常相似。主要的區(qū)別在于發(fā)出客戶端腳本的位置。在 <form> 元素開始后,在窗體的內(nèi)容前,記住用 RegisterClientScriptBlock 發(fā)出腳本。另一方面,RegisterStartupScript 在窗體的結(jié)尾 處、在所有窗體字段后,添加指定的腳本。使用 RegisterStartupScript 來放置與呈現(xiàn)的 HTML 元素交互的客戶端腳本。(稍后我將研究根據(jù)頁面加載將焦點設(shè)置到窗體字段的示例;要完成這個操作,您將要使用 RegisterStartupScript 方法。)

          RegisterClientScriptBlock 相似,由 RegisterStartupScript 添加的腳本塊需要一個唯一的密鑰值。同樣,該密鑰值主要由自定義控件開發(fā)人員使用。并不奇怪,還有一個 IsStartupScriptRegistered(key) 方法,它返回布爾值,表示帶有指定密鑰 的腳本塊是否已經(jīng)進行了注冊。

          有關(guān)使用 RegisterStartupScriptRegisterClientScriptBlock 來創(chuàng)建自定義編譯的服務(wù)器控件的詳細(xì)信息,請閱讀我以前的文章: Injecting Client-Side Script from an ASP.NET Server Control.

          RegisterArrayDeclaration(arrayName, arrayValue)

          如果需要創(chuàng)建帶有某些設(shè)置值的客戶端 JavaScript Array 對象,請使用該方法向特定的數(shù)組添加值。例如,當(dāng)使用 ASP.NET 網(wǎng)頁中的驗證控件時,就會構(gòu)建 Array 對象 (Page_Validators),以包含對頁面上驗證控件集合的引用。當(dāng)提交窗體時,就會枚舉該數(shù)組以檢查各種驗證控件是否有效。

          要將值 1、2 和 3 添加到名為 FavoriteNumbers 的客戶端 Array 對象,要使用以下服務(wù)器端代碼:

          RegisterArrayDeclaration("FavoriteNumbers", "1") RegisterArrayDeclaration("FavoriteNumbers", "2") RegisterArrayDeclaration("FavoriteNumbers", "3")

          這段代碼會發(fā)出以下客戶端腳本:

          <script language="javascript"> <!-- var FavoriteNumbers = new Array(1, 2, 3); // --> </script>

          請注意,被傳遞的每個數(shù)組值都必須是字符串;但是,呈現(xiàn)的客戶端腳本將 Array 對象的值設(shè)置為字符串的內(nèi)容。也就是說,如果希望創(chuàng)建帶有字符串值“Scott”和“Jisun”的 Array,要使用以下代碼:

          RegisterArrayDeclaration("FavoriteFolks", "'Scott'") RegisterArrayDeclaration("FavoriteFolks ", "'Jisun'")

          請注意,第二個輸入?yún)?shù)是包含 'Scott' 和 'Jisun' 的字符串 - 文本由單撇號分隔。這會顯示以下客戶端腳本:

          <script language="javascript"> <!-- var FavoriteFolks = new Array('Scott', 'Jisun'); // --> </script>
          RegisterHiddenField(hiddenFieldName, hiddenFieldValue)

          在傳統(tǒng)的 ASP 中,通常需要將各種信息從一個頁面分發(fā)到另一個頁面。完成這個操作的常用方法就是使用隱藏窗體字段。(隱藏窗體字段是不顯示的窗體字段,但是它的值會根據(jù)窗體的提交而發(fā)送。創(chuàng)建隱藏窗體字段的語法是 。)在 ASP.NET 中,通過自定義隱藏窗體字段傳遞信息的需要會極大地減少,因為頁面中的控件狀態(tài)會自動保持。但是,如果您發(fā)現(xiàn)需要創(chuàng)建自定義隱藏窗體字段,可以通過 RegisterHiddenField 方法來完成。

          RegisterHiddenField 方法接受兩個輸入?yún)?shù)。隱藏字段的名稱和值。例如,要創(chuàng)建帶有名稱 foo 和值 bar 的隱藏窗體字段,請使用以下代碼:

          RegisterHiddenField("foo", "bar")

          這會在頁面的 <form> 元素中添加隱藏窗體字段,如下所示:

          <form name="_ctl0" method="post" action="test.aspx" id="_ctl0"> <input type="hidden" name="foo" value="bar" /> ... </form>
          理解客戶端元素是如何呈現(xiàn)的

          Page 類包含負(fù)責(zé)呈現(xiàn)在上面討論的方法中注冊的客戶端腳本的兩個 internal 方法:OnFormRenderOnFormPostRender。(標(biāo)記為 internal 的方法只能被相同程序集中的其他類調(diào)用。因此,無法從 ASP.NET Web 應(yīng)用程序中的代碼隱藏類調(diào)用 Pageinternal 方法。)這兩個方法都在 HtmlForm 類的 RenderChildren 方法中進行調(diào)用。位于 System.Web.UI.HtmlControls 命名空間中的 HtmlForm 類表示 Web 窗體;也就是說,ASP.NET 網(wǎng)頁中的服務(wù)器端窗體<form runat="server">...</form> 在頁面的實例化階段中作為 HtmlForm 類的實例加載。

          因為由 Page 類的眾多方法注冊的客戶端腳本是在 OnFormRenderOnFormPostRender 方法中呈現(xiàn)的,并且因為這些方法只能由 HtmlForm 類進行調(diào)用,所以通過這些方法以編程方式添加的客戶端腳本,只有在網(wǎng)頁包含 Web 窗體時才會呈現(xiàn)。也就是說,通過上面討論的任意方法以編程方式添加的任意腳本元素,在 ASP.NET 網(wǎng)頁包含服務(wù)器端窗體(<form runat="server">...</form>)時只會在頁面的最后標(biāo)記中發(fā)出。

          通過首先添加開始 <form> 元素,會呈現(xiàn) ASP.NET 網(wǎng)頁上的 Web 窗體。之后,會調(diào)用 Web 窗體的 RenderChildren 方法,它包含三行代碼:

          Page.OnFormRender(...) MyBase.RenderChildren(...) Page.OnFormPostRender(...)

          Page 類的 OnFormRender 方法的調(diào)用會添加以下標(biāo)記:

          ?

          通過對 RegisterHiddenField 進行調(diào)用而添加的任意隱藏窗體字段。

          ?

          隱藏窗體字段中名為 __VIEWSTATE 的基于 64 位編碼的視圖狀態(tài)。

          ?

          通過對 RegisterClientScriptBlock 進行調(diào)用而添加的任意腳本塊。

          Web 窗體的 RenderChildren 方法中的第二行代碼調(diào)用基類的 RenderChildren 方法,它會在 Web 窗體中呈現(xiàn)內(nèi)容。在呈現(xiàn)所有窗體的內(nèi)容后,對 Page 類的 OnFormPostRender 方法進行調(diào)用,這將會添加以下客戶端內(nèi)容:

          ?

          RegisterArrayDeclaration 方法添加的任意 Array 對象。

          ?

          通過對 RegisterStartupScript 進行調(diào)用而添加的任意腳本塊。

          最后,在 Web 窗體的 RenderChildren 方法完成后,則會呈現(xiàn)關(guān)閉窗體標(biāo)記 ()。圖 2 以圖表形式說明了這個呈現(xiàn)過程。

          圖 2 假設(shè)您有些熟悉 ASP.NET 頁面的生命周期。如果您對學(xué)習(xí)更多有關(guān)頁面生命周期的知識感興趣,請考慮閱讀 Understanding ASP.NET View State,將重點放在標(biāo)題為“The ASP.NET Page Life Cycle”的節(jié)上。


          2. ASP.NET 中呈現(xiàn)的頁面

          分析腳本塊的呈現(xiàn)順序

          乍看 Page 類的注冊方法,在網(wǎng)頁中呈現(xiàn)的已注冊元素的順序好像是對應(yīng)于它們被添加到代碼中的順序。也就是說,設(shè)想 ASP.NET 網(wǎng)頁的代碼隱藏類中有以下兩行代碼:

          RegisterClientScriptBlock("showSaveMessage", _ "<script language=""JavaScript"">var name='" & _ someDataDrivenValue & "'; </script>") RegisterClientScriptBlock("showSaveMessage", _ "<script language=""JavaScript"">alert('Hello, ' + name + '!'); </script>")

          當(dāng)發(fā)現(xiàn)頁面呈現(xiàn)以下客戶端腳本塊(假設(shè) someDataDriveValue 的值為 Sam)時,您不要太奇怪:

          <script language="JavaScript">var name='Sam';</script> <script language="JavaScript">alert('Hello, ' + name + '!');</script>

          訪問該頁面的用戶將會看到一個警告框顯示“Hello, Sam!”。

          基于這個測試,您可能會認(rèn)為這樣的事實始終成立:在 HTML 頁面中發(fā)出腳本塊的順序就是在服務(wù)器端代碼中為它們指定的順序。但是,這是一個不正確的假設(shè),并且可以導(dǎo)致頁面中斷。例如,設(shè)想前面添加的腳本塊在 HTML 頁面中以相反的順序發(fā)出。那么,您將會得到:

          <script language="JavaScript">alert('Hello, ' + name + '!');</script> <script language="JavaScript">var name='Sam';</script>

          這將會顯示警告框“Hello, !”,因為變量 name 尚未分配值。顯然,有些時候發(fā)出腳本塊的順序非常重要的。

          Page 類的注冊方法 - RegisterClientScriptBlockRegisterStartupScriptRegisterArrayDeclarationRegisterHiddenFields - 全部將提供的腳本內(nèi)容寫入到內(nèi)部 HybridDictionary 中。HybridDictionary 是在 System.Collections.Specialized 命名空間中發(fā)現(xiàn)的數(shù)據(jù)結(jié)構(gòu),設(shè)計用于在有很多項目未知的字典中存儲項目。對于小型的項目集合,ListDictionary 是最有效的數(shù)據(jù)結(jié)構(gòu),但是對于較大的字典,Hashtable 會更有效。HybridDictionary 分離差異 - 它通過使用 ListDictionary 存儲項目來開始。當(dāng) ListDictionary 添加了它的第九個項目后,HybridDictionary 會從使用 ListDictionary 切換到使用 Hashtable

          盡管這種方法對于性能而言非常理想,但是如果您使用幾個腳本塊而且腳本塊的順序非常重要,那么它可能會帶來嚴(yán)重的破壞。這是因為,ListDictionary 維護元素被添加的順序,而 Hashtable 沒有進行維護。因此,如果您將八個或更少項目添加到任意一個特定的注冊方法中,項目將會以它們被添加的順序發(fā)出。但是,如果添加了第九個項目,腳本發(fā)出的順序看起來將是隨機的。

          ListDictionary 使用 linked list 存儲它的元素,而 Hashtable 將它的元素存儲在數(shù)組中,該數(shù)組的內(nèi)容按照字符串鍵的哈希值進行排序。有關(guān)鏈接列表和哈希表的完整討論已經(jīng)遠(yuǎn)遠(yuǎn)超出了本文所討論的范圍。有關(guān)詳細(xì)信息,包括對其性能的分析,請考慮閱讀 An Extensive Examination of Data Structures,尤其是 Part 2Part 4

          如果您計劃出現(xiàn)如下情況:可能會存在使用特殊注冊方法添加的多于八個客戶端元素,并且元素的出現(xiàn)順序比較重要,那么您可能希望研究一下 Peter Blum 的免費的 RegisterScripts 庫。對于所發(fā)出客戶端元素的順序,RegisterScripts 提供了更大的控制能力,并且還提供了無需手動添加 <script> 標(biāo)記的選項,當(dāng)利用 RegisterClientScriptBlockRegisterStartupScript 方法包括客戶端腳本時,您必須添加此標(biāo)記。

          根據(jù)對用戶操作的響應(yīng)執(zhí)行客戶端代碼

          對于插入頁面加載時運行的客戶端代碼而言,Page 類的注冊方法非常理想,但是在很多情況下,我們希望根據(jù)對最終用戶操作的響應(yīng)來運行代碼。例如,我們可能希望當(dāng)用戶單擊按鈕時顯示確認(rèn)對話框,或者當(dāng)下拉列表的選定項目發(fā)生變化時調(diào)用特殊的客戶端 JavaScript 功能。

          HTML 元素具有您可以點擊的大量客戶端事件,并且當(dāng)觸發(fā)事件時可以執(zhí)行客戶端代碼。所要求的標(biāo)記只是在 HTML 元素的標(biāo)記中作為一個屬性。例如,要在單擊某個按鈕時顯示警告框,可以添加以下代碼:

          <input type="button" value="Click me to see an alert box!" onclick="alert('Here it is!');" />

          要在客戶端事件觸發(fā)時運行客戶端代碼,可以將適當(dāng)?shù)膶傩蕴砑拥?HTML 元素中。對于 Web 控件,可以借助于編程方式使用 Attributes 集合添加客戶端屬性。例如,設(shè)想您具有一個 TextBox Web 控件,只要呈現(xiàn)的文本框獲得焦點,您就希望它突出顯示為黃色。要完成上述操作,您要將 TextBox Web 控件的呈現(xiàn) HTML 添加如下所示的代碼:

          <input type="text" onfocus="this.style.backgroundColor='yellow';" onblur="this.style.backgroundColor='white';" />

          要完成這個標(biāo)記,我們可以借助編程方式通過 Attributes 集合設(shè)置 TextBox Web 控件的 onfocusonblur 客戶端屬性,如下所示:

          TextBoxControl.Attributes("onfocus") = "this.style.backgroundColor='yellow';" TextBoxControl.Attributes("onblur") = "this.style.backgroundColor='white';"

          將客戶端代碼與客戶端事件結(jié)合在一起的這種技術(shù)通常用于提供豐富的、交互式的用戶體驗。稍后,我們將會在本文中說明如何使用這種技術(shù)來顯示基于用戶操作的確認(rèn)對話框。

          實現(xiàn)常用客戶端功能

          在我們研究 ASP.NET 方法(涉及動態(tài)地將客戶端腳本添加到網(wǎng)頁)后,讓我們將注意力轉(zhuǎn)移到這個知識。本文的其余部分重點講述常用客戶端任務(wù),例如顯示警告框、確認(rèn)框、彈出窗口等等。具體說來,我們將創(chuàng)建包含一組方法的類,這組方法可用在 ASP.NET 項目中,從而快速、便捷地提供這樣的功能。

          我們將要分析的、貫穿本文剩余部分的 Visual Basic .NET 代碼可以在本文的代碼下載中獲得。

          顯示警告框

          一個常用的客戶端要求就是顯示警告框。警告框是一個客戶端模式對話框,通常用于為最終用戶提供某些重要的信息。警告框的示例如圖 1 所示。警告框是通過客戶端 JavaScript alert 函數(shù)來進行顯示的,該函數(shù)接受一個單個的參數(shù),即要顯示的消息。顯示警告框相當(dāng)簡單和直接;實際上,本文前面已經(jīng)顯示了一個示例。

          為了使頁面開發(fā)人員盡可能簡單的顯示警告框,讓我們創(chuàng)建一個名為 ClientSidePage 的類,其中包含一個名為 DisplayAlert(message) 的方法。這個類將會繼承 Page 類。想要利用這些客戶端 helper 方法的頁面開發(fā)人員需要使他們的代碼隱藏類繼承這個 ClientSidePage 類,而不是繼承默認(rèn)的 Page 類。以下代碼顯示了帶有其第一個方法 DisplayAlertClientSidePage 類。

          Public Class ClientSidePage Inherits System.Web.UI.Page Public Sub DisplayAlert(ByVal message As String) RegisterClientScriptBlock(Guid.NewGuid().ToString(), _ "<script language=""JavaScript"">" & GetAlertScript(message) & "</script>") End Sub Public Function GetAlertScript(ByVal message As String) As String Return "alert('" & message.Replace("'", "\'") & "');" End Function End Class

          請注意,這個類派生自 System.Web.UI.Page 類。DisplayAlert 方法只是使用 RegisterClientScriptBlock 方法,在警告框中顯示提供的 message。由于這個方法可能會由單個頁面多次調(diào)用,每個調(diào)用將使用其密鑰的 GUID(是“全局唯一標(biāo)識符”的首字母縮寫)。傳遞到 alert 函數(shù)的字符串會使用撇號分隔,message 中的任意撇號都必須進行轉(zhuǎn)義(JavaScript 將撇號轉(zhuǎn)義為 \')。

          要將這段代碼用于 ASP.NET Web 應(yīng)用程序中,您需要將新類添加到 ASP.NET 應(yīng)用程序中。在 Visual Studio .NET 中,右鍵單擊解決方案資源管理器中的 ASP.NET Web 應(yīng)用程序項目名,然后選擇添加新類。然后,剪切上述代碼并將其粘貼到該類中。接下來,在要利用該代碼的 ASP.NET 網(wǎng)頁中,您需要修改代碼隱藏類,以便它從 ClientSidePage 類而不是從 Page 中繼承。以下代碼顯示了派生自 ClientSidePage 并使用 DisplayAlert 方法的示例代碼隱藏類。

          Public Class WebForm1 Inherits ClientSidePage Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load DisplayAlert("Hello, World!") End Sub ... End Class

          請注意,ClientSidePage 類不僅具有可以生成完整客戶端 <script> 元素的 DisplayAlert 方法,還具有可以返回客戶端腳本無 <script> 標(biāo)記的 GetAlertScript 方法。第二個方法可用于您希望基于某個客戶端事件顯示警告的情況中。例如,如果您希望在特定文本框接收到焦點的任意時間顯示警告,可以將以下代碼添加到服務(wù)器端代碼隱藏類中:

          TextBoxControlID.Attributes("onfocus") = GetAlertScript(message)
          將焦點設(shè)置為頁面加載時的窗體字段

          您是否注意到當(dāng)訪問 Google 時,焦點自動設(shè)置在搜索文本框呢?這一點小的“功能”使得搜索 Google 更快 - 在訪問 Google 時您不必再花費時間移動鼠標(biāo),然后單擊文本框。更確切的說,您只需在頁面加載時鍵入即可。將焦點設(shè)置到窗體字段(可以是文本框、單選按鈕、復(fù)選框或下拉列表)只要求幾行客戶端 JavaScript 代碼。讓我們將方法添加到 ClientSidePage 類,該類將在頁面加載時自動向指定的 Web 控件中添加焦點。這種方法需要發(fā)出如下所示的客戶端腳本:

          <script language="JavaScript"> function CSP_focus(id) { var o = document.getElementById(id); if (o != null) o.focus(); } </script> ... Form fields ... <input type="..." id="id of element to focus" ... /> ... Form fields ... <script language="JavaScript"> CSP_focus(id of element to focus); </script>

          客戶端函數(shù) CSP_focus 接受字符串參數(shù),窗體字段的 ID 被設(shè)置為焦點,并且從 DOM 中檢索 HTML 元素。然后,調(diào)用檢索元素的 focus() 函數(shù)。在網(wǎng)頁的底部,在指定所有窗體字段后,我們需要調(diào)用 CSP_focus 方法,該方法在想要設(shè)置焦點的窗體字段的 ID 中傳遞。

          下面的方法 GiveFocus(Control) 使用 RegisterClientScriptBlockRegisterStartupScript 方法來生成所需要的客戶端腳本。

          Public Sub GiveFocus(ByVal c As Control) RegisterClientScriptBlock("CSP-focus-function", _ "<script language=""JavaScript"">" & vbCrLf & _ "function CSP_focus(id) {" & _ " var o = document.getElementById(id); " & _ "if (o != null) o.focus(); " & _ "}" & vbCrLf & _ "</script>") RegisterStartupScript("CSP-focus", _ "<script language=""JavaScript"">CSP_focus('" & _ c.ClientID & "');</script>") End Sub

          要從其代碼隱藏類繼承 ClientSidePage 的 ASP.NET 網(wǎng)頁中使用 GiveFocus 方法,可以簡單地在 Page_Load 事件處理程序中調(diào)用 GiveFocus,并傳遞應(yīng)該在頁面加載時設(shè)置其焦點的 Web 控件。例如,要將焦點設(shè)置為 TextBox Web 控件 TextBoxControl,請使用以下 Page_Load 事件處理程序:

          Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load GiveFocus(TextBoxControl) End Sub
          打開彈出窗口

          盡管彈出窗口作為廣告發(fā)布者的工具在 Internet 上早已臭名昭著,但是很多 Web 應(yīng)用程序還是因為使用彈出窗口而得到了好處。例如,您想要某個頁面在 DataGrid 中顯示數(shù)據(jù)庫項目的列表,同時帶有可以編輯每個特定項目的鏈接。不再使用 DataGrid 的內(nèi)聯(lián)編輯功能,您可能希望在用戶選擇編輯 DataGrid 時打開彈出窗口,其中彈出窗口包含帶有可編輯 DataGrid 字段的文本框列表。(您希望這么做的一個原因在于可能存在大量的可編輯字段,但是您只想顯示 DataGrid 中最適當(dāng)?shù)淖侄危虼艘褂?DataGrid 的內(nèi)置編輯功能的可能性。)

          要顯示彈出窗口,請使用 JavaScript 函數(shù) window.open(),它使用很多可選輸入?yún)?shù),其中三個密切相關(guān)的參數(shù)是:

          ?

          加載彈出窗口的 URL。

          ?

          彈出窗口的字符串名稱。

          ?

          彈出窗口的特性,例如高度和寬度、窗口是否可以調(diào)整大小等等。

          window.open() 函數(shù)的完整討論已經(jīng)超出了本文的范圍;要學(xué)習(xí)更多內(nèi)容,請參閱技術(shù)文檔

          與其他顯示警告框的方法相似,ClientSidePage 類包含用于顯示彈出窗口的兩個方法 - 一個呈現(xiàn)顯示窗口的自包含 <script> 塊,另一個僅返回 JavaScript 腳本本身。除了打開彈出窗口的方法外,還有一組關(guān)閉當(dāng)前窗口的方法。(可能會出現(xiàn)這樣的情況,您希望以編程方式基于某些客戶端或服務(wù)器端事件來關(guān)閉彈出窗口。)

          Public Sub DisplayPopup(ByVal url As String, ByVal options As String) RegisterStartupScript(Guid.NewGuid().ToString(), _ "<script language=""JavaScript"">" & _ GetPopupScript(url, options) & _ "</script>") End Sub Public Function GetPopupScript(ByVal url As String, _ ByVal options As String) As String Return "var w = window.open(""" & _ url & """, null, """ & options & """);" End Function Public Sub CloseWindow(Optional ByVal refreshParent As Boolean = False) RegisterClientScriptBlock("CSP-close-popup", _ "<script language=""JavaScript"">" & _ GetCloseWindowScript(refreshParent) & "</script>") End Sub Public Function GetCloseWindowScript(Optional _ ByVal refreshParent As Boolean = False) As String Dim script As String If refreshParent Then script &= "window.opener.location.reload();" End If Return "self.close();" End Function

          該代碼的執(zhí)行示例可以在本文的代碼下載中找到。同時,您還將發(fā)現(xiàn)一個示例網(wǎng)頁,它具有一個在與 ASP.NET 網(wǎng)頁相同的目錄中列出文件的 DataGrid。這個 DataGrid 具有兩列:顯示超級鏈接的 TemplateColumn,當(dāng)單擊時打開顯示所選文件內(nèi)容的彈出窗口;以及該文件的名稱(如圖 3 所示)。


          3. 帶有彈出窗口的 DataGrid

          DataGrid 的標(biāo)記利用 GetPopupScript 方法,如下所示:

          <asp:DataGrid id="dgFiles" runat="server" ...> <Columns> <asp:TemplateColumn HeaderText="View"> <ItemTemplate> <a href='javascript:<%# GetPopupScript("ViewFile.aspx?FileName=" & DataBinder.Eval(Container.DataItem, "Name"), "scrollbars=yes,resizable=yes,width=500,height=400") %>'> View File</a> </ItemTemplate> </asp:TemplateColumn> <asp:BoundColumn DataField="Name" HeaderText="Filename"></asp:BoundColumn> </Columns> </asp:DataGrid>

          ASP.NET 網(wǎng)頁 ViewFile.aspx 打開其名稱在 querystring 中指定的文件并顯示其內(nèi)容(如圖 4 所示)。


          4. 在彈出窗口中顯示 Web.config 的內(nèi)容

          彈出窗口最適用于只有 Intranet 應(yīng)用程序的情況,因為很多 Internet 用戶利用某種彈出阻止軟件,例如 Google 工具欄。實際上,利用 Microsoft Windows XP Service Pack 2,Microsoft Internet Explorer 將會在默認(rèn)情況下配置為阻止彈出窗口。但是,當(dāng)用戶訪問受信任站點或本地 Intranet 區(qū)域中的站點時,彈出窗口將仍然會出現(xiàn)。有關(guān)在 Windows XP Service Pack 2 中阻止 Internet Explorer 彈出窗口功能的詳細(xì)信息,請務(wù)必閱讀 Changes to Functionality in Microsoft Windows XP Service Pack 2

          在回發(fā)前確認(rèn)

          在本文的前面部分,我們研究了如何顯示客戶端警告框,這是帶有“OK”按鈕的模式對話框。JavaScript 提供被稱為確認(rèn)對話框的更具有交互風(fēng)格的警告框。使用 confirm(message) 函數(shù)顯示確認(rèn)對話框并通過 message 輸入?yún)?shù)與“OK”和“Cancel”按鈕指定顯示帶有文本的對話框的效果。如果用戶單擊“OK”,confirm(message) 函數(shù)會返回 true;如果他們單擊“Cancel”,則返回 false。

          通常情況下,確認(rèn)對話框用于確保用戶在提交窗體之前希望繼續(xù)。當(dāng)單擊 HTML 元素(例如提交按鈕)提交窗體時,如果 HTML 元素觸發(fā)返回 false 的客戶端事件處理程序,窗體提交就被取消。通常,確認(rèn)對話框用于網(wǎng)頁中,如下所示:

          <form ...> <input type="submit" value="Click Me to Submit the Form" onclick="return confirm('Are you sure you want to submit this form?');" /> </form>

          當(dāng)用戶單擊“Click Me to Submit the Form”按鈕時,他們將會看到確認(rèn)對話框,詢問他們是否確實希望提交該窗體(如圖 5 所示)。如果用戶單擊“OK”,confirm() 將返回 true,該窗體將被提交。但是,如果他們單擊“Cancel”按鈕,confirm() 將返回 false,該窗體提交將被取消。


          5. 確認(rèn) JavaScript 的結(jié)果

          設(shè)想您具有一個 DataGrid帶有標(biāo)簽為“Delete”的一列按鈕。在單擊該按鈕時,該窗體將會回發(fā),并且選定的記錄將被刪除。在此例中,您可能希望復(fù)查用戶是否確實希望刪除該記錄。此時,要使用客戶端確認(rèn)對話框?qū)浅@硐搿D梢杂脤υ捒蛱崾居脩簦暶魅缦滤镜膬?nèi)容:“This will permanently delete the record.Are you sure you want to continue?”如果用戶單擊“OK”,該窗體將會回發(fā),并且記錄將被刪除;如果他們單擊“Cancel”,該窗體將不會回發(fā),而且記錄也不會被刪除。

          要添加在單擊按鈕后顯示確認(rèn)對話框所必需的客戶端 JavaScript,請簡單地使用 Attributes 集合來添加客戶端 onclick 事件處理程序。具體說來,要將 onclick 事件處理程序代碼設(shè)置為:return confirm(message);。為了提供 DataGridButtonColumn 的這種功能,您需要在 DataGridItemCreatedItemDataBound 事件處理程序中以編程方式引用 ButtonLinkButton 控件,并且在那里設(shè)置 onclick 屬性。有關(guān)詳細(xì)信息,請參閱 http://aspnet.4guysfromrolla.com/articles/090402-1.aspx

          確認(rèn) AutoPostBack DropDownLists

          盡管通常在單擊按鈕時使用確認(rèn)對話框,但是還可以在更改下拉列表時使用它們。例如,您可能具有一個當(dāng)特定的 DropDownList Web 控件發(fā)生更改時會自動回發(fā)的網(wǎng)頁。(DropDownList Web 控件具有一個 AutoPostBack 屬性,如果設(shè)置為 True,只要 DropDownList 的選定項目發(fā)生更改就會導(dǎo)致窗體回發(fā)。)

          直觀地講,您可能認(rèn)為對 DropDownList 添加確認(rèn)對話框與對 Button Web 控件添加這種對話框相同。也就是說,簡單地將 DropDownList 的客戶端 onchange 屬性更改為如下內(nèi)容:return confirm(...);。使用:

          DropDownListID.Attributes("onchange") = "return confirm(...);"

          遺憾的是,這并不會按期望工作,因為 AutoPostBackDropDownListonchange 屬性將設(shè)置為會導(dǎo)致回發(fā)的 JavaScript,即對客戶端 __doPostBack 函數(shù)的調(diào)用。當(dāng)您自己借助編程方式設(shè)置 onchange 屬性時,最后的結(jié)果是呈現(xiàn)的客戶端 onchange 事件處理程序同時具有您的代碼和對 __doPostBack 的調(diào)用:

          <select onchange="return confirm(...);__doPostBack(...);"> ... </select>

          記住,我們確實希望發(fā)生的情況是,如果確認(rèn)返回 true,就調(diào)用 __doPostBack 函數(shù),因為之后頁面將會被回發(fā)。通過利用 Attributes 集合將 onchange 事件設(shè)置為:if (confirm(...)),我們可以完成這一操作,而該代碼會生成以下標(biāo)記,該標(biāo)記正是我們所希望的:

          <select onchange="if (confirm(...)) __doPostBack(...);"> ... </select>

          乍看起來,這似乎會具有所期望的效果。如果用戶從下拉列表中選擇不同的項目,將會出現(xiàn)一個確認(rèn)框。如果用戶單擊“OK”,該窗體將回發(fā);如果用戶單擊“Cancel”,該窗體回發(fā)會暫停。盡管問題在于下拉列表維持用戶選定的項目以啟動下拉列表的 onchange 事件。例如,設(shè)想下拉列表加載正在進行選擇的項目 x,然后用戶選擇項目 y。這將會觸發(fā)下拉列表客戶端 onchange 事件,它將會顯示確認(rèn)對話框。現(xiàn)在,設(shè)想用戶點擊“Cancel”- 下拉列表將仍然選擇項目 y。我們希望的是將選擇轉(zhuǎn)回到項目 x。

          要實現(xiàn)此目的,我們需要做兩件事情:

          1.

          編寫一個“記住”選定下拉列表項目的 JavaScript 函數(shù)。

          2.

          在下拉列表的客戶端 onchange 事件中,如果用戶單擊“Cancel”,您需要將下拉列表轉(zhuǎn)換回“已記住的”值。

          步驟 1 必須為下拉列表和函數(shù)(當(dāng)頁面加載時運行,并且記錄下拉列表的值)創(chuàng)建全局腳本變量。步驟 2 要求為下拉列表的客戶端 onchange 屬性更改為如下所示內(nèi)容:

          if (!confirm(...)) resetDDLIndex(); else __doPostBack();

          其中 resetDDLIndex 是 JavaScript 函數(shù),它將下拉列表選定的值返回到“已記住的”值。用于此目的的客戶端腳本應(yīng)該如下所示:

          <select id="ddlID" onchange="if (!confirm(...)) resetDDLIndex(); else __doPostBack(...);"> ... </select> <script language="JavaScript"> var savedDDLID = document.getElementById("ddlID").value; function resetDDLIndex() { document.getElementById("ddlID").value = savedDDLID; } </script>

          通過在 ClientSidePage 類中創(chuàng)建 helper 方法,這個必要的腳本可以輕松地生成。

          Public Sub ConfirmOnChange(ByVal ddl As DropDownList, ByVal message As String) 'Register the script block If Not IsStartupScriptRegistered("CSP-ddl-onchange") Then RegisterStartupScript("CSP-ddl-onchange", _ "<script language=""JavaScript"">" & _ "var CSP_savedDDLID = " & _ document.getElementById('" & _ ddl.ClientID & "').value;" & vbCrLf & _ "function resetDDLIndex() {" & vbCrLf & _ " document.getElementById('" & " & _ " ddl.ClientID & "').value = CSP_savedDDLID;" & vbCrLf & _ "}" & vbCrLf & _ "</script>") End If ddl.Attributes("onchange") = _ "if (!confirm('" & message.Replace("'", "\'") & _ "')) resetDDLIndex(); else " End Sub

          要使用這段代碼,簡單地調(diào)用網(wǎng)頁上每個 AutoPostBackDropDownList 的該方法,當(dāng)網(wǎng)頁上的選定項目發(fā)生更改時要在該網(wǎng)頁上顯示對話框。

          未保存而退出時進行確認(rèn)

          在我所創(chuàng)建的大多數(shù)每個數(shù)據(jù)驅(qū)動的 Web 應(yīng)用程序中,始終會有用戶可以編輯數(shù)據(jù)庫特定信息的特定頁面。非常簡單的一個示例是帶有一系列 TextBoxDropDownList Web 控件的頁面,而數(shù)據(jù)庫數(shù)據(jù)填充在這些控件中。用戶可以進行任何適當(dāng)?shù)男薷模缓髥螕簟癝ave”按鈕將他們所做的更改保存到數(shù)據(jù)庫。

          當(dāng)我創(chuàng)建這些頁面時,通常會以兩個 Button Web 控件來結(jié)束頁面:“Save”按鈕和“Cancel”按鈕。“Save”按鈕將任意更改保存回數(shù)據(jù)庫,而“Cancel”按鈕不保存任何更改退出頁面。盡管兩個按鈕看起來可能是一個完美的設(shè)計,但有時用戶會在他們想要單擊“Save”按鈕時意外地單擊“Cancel”按鈕,這樣就會丟失了他們對數(shù)據(jù)所做的所有更改。為防止這種情況發(fā)生,可以在“Cancel”按鈕上使用確認(rèn)框,它只有在網(wǎng)頁上的任意文本框或下拉列表發(fā)生更改時才會出現(xiàn)。也就是說,如果用戶對數(shù)據(jù)進行了任意更改,然后單擊“Cancel”,確認(rèn)框?qū)崾舅麄兪欠翊_實要在不保存的情況下退出。(如果用戶只是單擊“Cancel”,而沒有更改任何數(shù)據(jù),將不會顯示這樣的確認(rèn)框。)

          這種用戶體驗可以通過少量客戶端 JavaScript 來實現(xiàn)。基本上可以說,它需要一個 JavaScript 全局變量 isDirty,在初始時為 false,但只要觸發(fā)窗體字段的 onchange 事件,它就會設(shè)置為 true。如果 isDirty 為 true,則還有一個顯示確認(rèn)對話框的 JavaScript 函數(shù)。“Cancel”按鈕的 onclick 客戶端事件處理程序限定為從該 JavaScript 函數(shù)返回結(jié)果。以下 HTML 說明了這個概念:

          <script language="JavaScript"> var isDirty= false; function checkForChange(msg) { if (isDirty) return confirm(msg); else return true; } </script> Name: <input type="text" onchange="isDirty = true;" /> <input type="submit" name="btnSave" value="Save" id="btnSave" /> <input type="submit" name="btnCancel" value="Cancel" id="btnCancel" onclick="return checkForChange('You have made changes to the data since last saving. If you continue, you will lose these changes.');" />

          可以通過將該腳本生成移動到 ClientSidePage 類,簡單地生成這個腳本。具體說來,我們可以創(chuàng)建下列三個方法:

          Protected Sub RegisterOnchangeScript() If Not IsClientScriptBlockRegistered("CSP-onchange-function") Then RegisterClientScriptBlock("CSP-onchange-function", _ "<script language=""JavaScript"">" & _ "var isDirty= false;" & vbCrLf & _ "function CSP_checkForChange(msg) {" & vbCrLf & _ " if (isDirty) return confirm(msg); " & _ "else return true;" & vbCrLf & _ "}" & vbCrLf & _ "</script>") End If End Sub Public Sub MonitorChanges(ByVal c As WebControl) RegisterOnchangeScript() If TypeOf c Is CheckBox Or TypeOf c Is CheckBoxList _ Or TypeOf c Is RadioButtonList Then c.Attributes("onclick") = "isDirty = true;" Else c.Attributes("onchange") = "isDirty = true;" End If End Sub Public Sub ConfirmOnExit(ByVal c As WebControl, ByVal message As String) RegisterOnchangeScript() c.Attributes("onclick") = _ "return CSP_checkForChange('" & message.Replace("'", "\'") & "');" End Sub

          要創(chuàng)建表現(xiàn)這個行為的網(wǎng)頁,我們只需要將其服務(wù)器端代碼隱藏類派生自 ClientSidePage,并且在 Page_Load 事件處理程序中,為需要客戶端 onchange 事件的每個 Web 控件對 MonitorChanges 進行調(diào)用,并且為在單擊時應(yīng)該顯示警告用戶是否進行更改并退出頁面的每個 ButtonLinkButtonImageButton,對 ConfirmOnExit 進行調(diào)用。

          MonitorChanges 方法使用 onclick 客戶端事件,而不是用于 CheckBoxCheckBoxListRadioButtonList Web 控件的 onchange。這是因為這些控件將 <span> 標(biāo)記或 <table> 限制在復(fù)選框或很多復(fù)選框或單選按鈕附近。在我利用 Internet Explorer 進行測試時,我發(fā)現(xiàn)在應(yīng)用到 <span> 或 <table> 時,選中復(fù)選框或單擊單選按鈕并沒有選擇 onchange 事件,但是卻觸發(fā)了 onclick 事件。

          圖 6 顯示了帶有兩個 TextBox Web 控件、一個 DropDownList Web 控件和一個 CheckBox Web 控件的 ASP.NET 網(wǎng)頁示例。如下面的 Page_Load 事件處理程序所示,所有這些 Web 控件都會被監(jiān)視所做的更改。配置“Cancel”按鈕 btnCancel,這樣如果在進行更改后單擊它,將顯示一個確認(rèn)對話框。


          6. 帶有確認(rèn)的對話框

          Public Class ConfirmOnExit Inherits ClientSidePage Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles MyBase.Load 'Specify what controls to check for changes MonitorChanges(name) MonitorChanges(age) MonitorChanges(favColor) MonitorChanges(chkSilly) ConfirmOnExit(btnCancel, _ "You have made changes to the data since last saving." & _ " If you continue, you will lose these changes.") End Sub ... End Class

          客戶端 onchange 事件不能用于 Netscape 的舊版本中。同樣,Internet Explorer 5.0 的 onchange 事件也具有一些已報告的問題(在 Internet Explorer 5.01 SP 1 中已經(jīng)修復(fù))。

          而且,這種方法將不會像利用 DropDownList Web 控件那樣將 AutoPostBack 設(shè)置為 True,而是回發(fā)將重置 isDirty 的值。這個問題有很多解決方案,例如使用隱藏窗體字段,指示出回發(fā)窗體數(shù)據(jù)是否以 dirty 開始。我將實現(xiàn)這個操作的過程作為練習(xí)留給讀者。

          創(chuàng)建客戶端 MessageBox 控件

          由于確認(rèn)對話框是一種防止意外點擊的非常好的方法,可以潛在地降低到 Web 服務(wù)器的回發(fā)數(shù)量,有幾種特定方案您可能希望顯示確認(rèn)對話框,并且能夠在服務(wù)器端確定用戶是否單擊了“OK”或“Cancel”。(記住,對于確認(rèn)對話框,如果用戶單擊“Cancel”,該窗體則不會回發(fā)。)而且,JavaScript 中的警告和確認(rèn)框在外觀上非常受限。幸運的是,客戶端 VBScript 通過其 MsgBox 函數(shù)提供了更豐富的消息框體驗。

          在過去的項目中,我需要客戶端模式消息框,無論單擊什么按鈕,它都可以引起回發(fā)。作為響應(yīng),我構(gòu)建了自定義編譯的 ASP.NET 服務(wù)器控件來滿足這些要求。此外,客戶端消息框還使用 VBScript 的 MsgBox 函數(shù)來提供更豐富的消息框體驗。

          在 Microsoft Internet Explorer 瀏覽器中,VBScript 只作為客戶端腳本編輯語言。要考慮到這一點,如果訪問瀏覽器是 Internet Explorer,那么我的服務(wù)器控件只能使用 VBScript。如果是非 Internet Explorer 瀏覽器,則服務(wù)器端控件使用 JavaScript。

          對這種自定義服務(wù)器控件深入的討論可以完全保證整個文章的正確性,因此無需將重點放在控件的內(nèi)部工作原理上,讓我們分析如何在 ASP.NET 網(wǎng)頁中使用 MessageBox 控件。(控件的完整資源以及使用該控件的示例 ASP.NET 網(wǎng)頁都可以從本文的下載中獲得。)

          要在 ASP.NET 網(wǎng)頁中使用 MessageBox 控件,首先要將 MessageBox 控件添加到 Visual Studio .NET 工具箱。通過右鍵單擊工具箱、從工具箱中選擇“Add/Remove Items”、然后瀏覽到 MessageBox 程序集可以實現(xiàn)上述任務(wù)。要將客戶端消息框添加到網(wǎng)頁,只要將其從工具箱拖動到該設(shè)計器即可。圖 7 顯示了 Visual Studio .NET 設(shè)計器中的 MessageBox 控件。


          7. 顯示模式消息框

          MessageBox 類具有很多可以進行配置的屬性,以調(diào)整消息框的外觀:

          ?

          Buttons. 指定要顯示的按鈕。ButtonOptions 枚舉中定義的選項可以為:OkOnly、OkCancel、AbortRetryIgnore、YesNoCancel、YesNo 或 RetryCancel。

          ?

          DisplayWhenButtonClicked. 點擊后將顯示客戶端消息框的按鈕 Web 控件的 ID。如果希望由于點擊某個特定按鈕而顯示消息框,請使用這個屬性。

          ?

          Icon. 顯示在消息框中的圖標(biāo);選項定義于 IconOptions 枚舉中。有效的值為:Critical、Question、Exclamation 和 Information。

          ?

          Prompt. 顯示在消息框中的文本。

          ?

          Title. 消息框的標(biāo)題。

          一旦將 MessageBox 控件添加到 ASP.NET 網(wǎng)頁,下一個挑戰(zhàn)就是使其根據(jù)特定客戶端操作進行顯示。DisplayWhenButtonClicked 屬性允許您指定頁面上按鈕 Web 控件的 ID點擊后將會顯示消息框。另外,您還可以通過調(diào)用客戶端函數(shù) mb_show(id) 來顯示消息框,其中 IDMessageBox 控件的 ID

          不管您選擇在消息框中顯示什么按鈕配置,當(dāng)單擊任意按鈕時,隨后就會發(fā)生回發(fā)并且觸發(fā) MessageBox 控件的 Click 事件。通過在設(shè)計器中簡單地雙擊 MessageBox,可以為此事件創(chuàng)建一個事件處理程序。事件處理程序的第二個輸入?yún)?shù)是類型 MessageBoxClickedEventArgs,它包含一個返回用戶單擊消息框按鈕的信息的 ButtonClicked 屬性。

          MessageBox 控件在如下情況下非常有用,當(dāng)您希望快速為用戶提供模式對話框,而不管用戶的選擇以及回發(fā)中的結(jié)果。要查看操作中的 MessageBox 控件,請簽出源代碼下載中的 MsgBoxDemo.aspx 頁面。

          小結(jié)

          本文一開始就研究了網(wǎng)頁中客戶端腳本的常見使用,然后轉(zhuǎn)向分析將客戶端腳本插入到 ASP.NET 網(wǎng)頁中的方法和技術(shù)。正如我們看到的那樣,Page 類包含很多旨在借助編程方式從服務(wù)器端代碼隱藏類插入客戶端腳本塊的方法。這些方法也常用于自定義編譯的服務(wù)器控件,請參閱我以前文章中的討論: Injecting Client-Side Script from an ASP.NET Server Control.

          除了添加腳本塊外,客戶端功能通常必須與由某些 HTML 元素引發(fā)的客戶端事件結(jié)合在一起。要借助編程方式通過服務(wù)器端代碼隱藏類指定 Web 控件的客戶端事件處理程序,請使用 Attributes 集合,它可用作所有 Web 控件的屬性。

          本文的后半部分應(yīng)用了前半部分中涉及的內(nèi)容,顯示了如何在 ASP.NET 網(wǎng)頁中實現(xiàn)常用客戶端功能。我們看到了如何擴展 Page 類,這樣我們可以利用代碼隱藏類輕松地顯示警告框、將頁面加載上的焦點設(shè)置到特定 Web 控件、如何顯示彈出窗口以及如何顯示確認(rèn)對話框。我們還研究了創(chuàng)建自定義服務(wù)器控件,它使用 VBScript 來提供更豐富的客戶端消息框用戶體驗,而且無需考慮單擊按鈕就可以導(dǎo)致回發(fā)。

          祝大家編程愉快!

          特別感謝...

          在我將文章提交給 MSDN 編輯之前,有很多志愿者幫助我對本文進行校對并為本文的內(nèi)容、語法和目的提供了反饋。本文校對過程中的主要貢獻者包括 Maxim KarpovCarlos SantosMilan Negovan 和 Carl Lambrecht。如果您有興趣加入到不斷增長的校對人員中,請給我發(fā)郵件 mitchell@4guysfromrolla.com

          關(guān)于作者

          Scott Mitchell 著有五本書,他是 4GuysFromRolla.com 網(wǎng)站的創(chuàng)始人,過去五年來一直從事 Microsoft Web 技術(shù)方面的研究。Scott 是一位獨立的顧問、培訓(xùn)師和作家。您可以通過 mitchell@4guysfromrolla.com 與作者進行聯(lián)絡(luò),或者通過作者的 blog 進行聯(lián)絡(luò),其網(wǎng)址是:http://ScottOnWriting.NET

          posted on 2005-12-08 21:16 天外飛仙 閱讀(470) 評論(0)  編輯  收藏 所屬分類: .net
          主站蜘蛛池模板: 和田市| 顺平县| 济南市| 清河县| 文成县| 闽清县| 抚宁县| 富蕴县| 吉安县| 海口市| 个旧市| 永春县| 青阳县| 延津县| 乐山市| 百色市| SHOW| 喀喇| 同心县| 井研县| 昌邑市| 巴林左旗| 曲水县| 鄢陵县| 潞城市| 界首市| 临汾市| 保山市| 莲花县| 寿阳县| 铜梁县| 西丰县| 湘潭市| 崇文区| 保靖县| 阿克陶县| 云和县| 濉溪县| 荣昌县| 理塘县| 石泉县|