天天看點

asp.net夜話之五:Page類和回調技術

        asp.net夜話之五:Page類和回調技術

        在今天我主要要介紹的有如下知識點:

Page類介紹

Page的生命周期

IsPostBack屬性

ClientScriptManager類

回調技術(CallBack)

        Page類介紹

        asp.net有時候也被成為WebForm,因為開發一個asp.net頁面就像開發一個WinFrom窗體一樣,我們同樣可以采用拖拽控件、輕按兩下産生相關處理代碼的方法。在asp.net中,建立一個頁面可以采用兩種模型。

單頁模型

        用Dreamweaver建立的asp.net頁面就是單頁模型,當然利用Visual Studio 2005也能建立單頁模型,不過在Visual Studio 2005中建立的頁面預設不是單頁模型,要想在Visual Studio 2005建立單頁模型的網頁如下:

        注意確定“将代碼放在單獨的檔案中”選項處于未選中狀态,預設情況下這個選項是處于選中狀态的。這樣就建立了單頁模型的網頁。

此時的頁面代碼如下:

     1. <%@ Page Language="C#" %> 

     2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

     3. <script runat="server"> 

     4. </script> 

     5. <html xmlns="http://www.w3.org/1999/xhtml" > 

     6. <head runat="server"> 

     7.         <title>無标題頁</title> 

     8. </head> 

     9. <body> 

    10.         <form id="form1" runat="server"> 

    11.         <div> 

    12.            

    13.         </div> 

    14.         </form> 

    15. </body> 

    16. </html>

        注意在頁面中有這樣一句代碼:

     1. <script runat="server"> 

     2. </script>

        這句代碼與普通javascript語句塊不同的是有一個runat="server"屬性,表示這裡的代碼是在伺服器上運作的C#代碼。切換到設計視圖,然後輕按兩下頁面,然後這部分會變成如下的樣子:

     2.         protected void Page_Load(object sender, EventArgs e) 

     3.         { 

     4.         } 

     5. </script>

        其中Page_Load就是頁面加載的時候在伺服器上運作的方法。

        單頁模型的特點是HTML标記、控件代碼及伺服器端運作的C#代碼全部包含在一個aspx頁面中,Web伺服器第一次運作該頁面的時候會将這個頁面生成一個類檔案,對于上面的Index.aspx頁面,會生成 ASP.Index_aspx的類,然後再将這個ASP.Index_aspx類編譯成IL代碼,Web伺服器通過CLR(Common Language Runtime,通用語言運作環境)運作相應的IL代碼。

        單頁模型的缺點是頁面和代碼混在一起,維護起來較為麻煩。

代碼頁面分離模式

        代碼頁面模式就是将頁的标記(HTML代碼)和伺服器端元素放在.aspx頁面中,而也代碼在位于一個.aspx.cs中。采用預設方式建立的aspx網頁就是這種方式。

        下面就是一個采用代碼頁面分離模式建立的Home.aspx頁面的代碼:

     1. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Home.aspx.cs" Inherits="Home" %> 

     3. <html xmlns="http://www.w3.org/1999/xhtml" > 

     4. <head runat="server"> 

     5.         <title>無标題頁</title> 

     6. </head> 

     7. <body> 

     8.         <form id="form1" runat="server"> 

     9.         <div> 

    10.            

    11.         </div> 

    12.         </form> 

    13. </body> 

    14. </html>

        其對應的頁代碼是:

     1. using System; 

     2. using System.Data; 

     3. using System.Configuration; 

     4. using System.Collections; 

     5. using System.Web; 

     6. using System.Web.Security; 

     7. using System.Web.UI; 

     8. using System.Web.UI.WebControls; 

     9. using System.Web.UI.WebControls.WebParts; 

    10. using System.Web.UI.HtmlControls; 

    11. public partial class Home : System.Web.UI.Page 

    12. { 

void Page_Load() void Page_Load(object sender, EventArgs e) 

    14.         { 

    15.         } 

    16. }

        首先要關注的aspx的頭部分代碼:

   1. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Home.aspx.cs" Inherits="Home" %>

        @Page是一個頁面指令,在這裡Language="C#"指明了目前頁面采用的背景代碼是C#語言,CodeFile="Home.aspx.cs"表示這個頁面對應的頁代碼檔案是Home.aspx.cs這個檔案,Inherits="Home" 表示目前aspx頁繼承自Home這個類。

現在再關注一下頁代碼檔案聲明:

        1. public partial class Home : System.Web.UI.Page

從這部分代碼可以看出Home類是繼承自System.Web.UI.Page類的。注意這裡還有一個C#2.0的關鍵字partial,這個關鍵字表示目前代碼是一個局部類,以表示這個類是構成整個Web頁面窗體的一部分。Web伺服器運作這個頁面的時候最終會将aspx頁面和對應的頁代碼編譯成一個類檔案,然後生成IL代碼。

代碼頁面分離模式的好處是頁面展示部分和邏輯控制部分的代碼分離開來,便于管理和維護,這也是微軟推薦的開發方式。

asp.net頁面的聲明周期

        asp.net頁面運作的時候将經曆一個聲明周期,這個生命周期中會進行一系列的操作,調用一系列的方法。了解asp.net頁面的生命周期對于精确控制頁面的控件呈現方式和行為非常重要。

        一般說來一個正常頁面要經曆如下幾個生命周期階段:

階段  說明

        頁請求  頁請求發生在頁生命周期開始之前。使用者請求頁時,ASP.NET 将确定是否需要分析和編譯頁(進而開始頁的生命周期),或者是否可以在不運作頁的情況下發送頁的緩存版本以進行響應。

        開始  在開始階段,将設定頁屬性,如 Request 和 Response。在此階段,頁還将确定請求是回發請求還是新請求,并設定 IsPostBack 屬性。此外,在開始階段期間,還将設定頁的 UICulture 屬性。

        頁初始化  頁初始化期間,可以使用頁中的控件,并将設定每個控件的 UniqueID 屬性。此外,任何主題都将應用于頁。如果目前請求是回發請求,則回發資料尚未加載,并且控件屬性值尚未還原為視圖狀态中的值。

        加載  加載期間,如果目前請求是回發請求,則将使用從視圖狀态和控件狀态恢複的資訊加載控件屬性。

        驗證  在驗證期間,将調用所有驗證程式控件的 Validate 方法,此方法将設定各個驗證程式控件和頁的 IsValid 屬性。

        回發事件處理  如果請求是回發請求,則将調用所有事件處理程式。

        呈現  在呈現期間,視圖狀态将被儲存到頁,然後頁将調用每個控件,以将其呈現的輸出提供給頁的 Response 屬性的 OutputStream。

        解除安裝  完全呈現頁、将頁發送至用戶端并準備丢棄時,将調用解除安裝。此時,将解除安裝頁屬性(如 Response 和 Request)并執行清理。

        在頁的生命周期中,一般會有如下事件:

頁事件  典型使用

Page_PreInit  使用 IsPostBack 屬性确定是否是第一次處理該頁。

建立或重新建立動态控件。

動态設定主要頁。

動态設定 Theme 屬性。

讀取或設定配置檔案屬性值。

注意:如果請求是回發請求,則控件的值尚未從視圖狀态還原。如果在此階段設定控件屬性,則其值可能會在下一階段被改寫。

Page_Init  讀取或初始化控件屬性。

Page_Load  讀取和更新控件屬性。

Control events  執行特定于應用程式的處理:

如果頁包含驗證程式控件,請在執行任何處理之前檢查頁和各個驗證控件的 IsValid 屬性。

處理特定事件,如 Button 控件的 Click 事件。

Page_PreRender  對頁的内容進行最後更改。

Page_Unload  執行最後的清理工作,可能包括:

關閉打開的檔案和資料庫連接配接。

完成日志記錄或其他特定于請求的任務。

        需要注意的是,每個asp.net控件也有與asp.net類似的生命周期,如果aspx頁面中包含有asp.net伺服器控件,那麼在調用頁面的方法時也會調用控件的相關方法。

另外,Web應用程式是無狀态的。每次請求一個新網頁或者重新整理頁面伺服器都會建立一個目前頁的新執行個體,這就意味着無法擷取頁面的以前的資訊,如果确實需要這麼做,需要采用額外的機制。

        我們将剛才建立的Index.aspx頁面中添加代碼,如下:

     4.         string date; 

     5.         protected void Page_Load(object sender, EventArgs e) 

     6.         { 

     7.                 if (date == null)//如果date為空則設定為目前時間的字元串形式 

     8.                 { 

     9.                         date = DateTime.Now.ToString(); 

    10.                 } 

    11.                 Response.Write("目前時間:"+date); 

    12.         } 

    13. </script> 

    14. <html xmlns="http://www.w3.org/1999/xhtml" > 

    15. <head runat="server"> 

    16.         <title>無标題頁</title> 

    17. </head> 

    18. <body> 

    19.         <form id="form1" runat="server"> 

    20.         <div> 

    21.            

    22.         </div> 

    23.         </form> 

    24. </body> 

    25. </html>

        按照正常了解,第一次運作的時候date字元串為null,會被設定成系統目前的字元串表示形式,并且輸出,再次重新整理的時候date字元串不再為空,會依然輸出剛才的時間字元串,但是結果卻不是這樣。第一次運作的結果:

        重新整理頁面之後的結果:

        這就證明了即使是重新整理目前頁也會重新生成一個目前頁面的執行個體,因為隻有在生成頁面新執行個體的情況下date字元串變量才為空,才會被重新設定值。

        Page類有一個IsPostBack屬性,這個屬性用來訓示目前頁面是第一次加載還是響應了頁面上某個控件的伺服器事件導緻回發而加載。

這次我們繼續對Index.aspx頁面添加代碼,在頁面中增加了一個Button控件,如下:

    12.                 if (!Page.IsPostBack) 

    13.                 { 

    14.                         Response.Write("第一次加載。"); 

    15.                 } 

    16.                 else 

    17.                 { 

    18.                         Response.Write("響應用戶端回發而加載。"); 

    19.                 } 

    20.         } 

    21.         protected void btnOK_Click(object sender, EventArgs e) 

    22.         { 

    23.         } 

    24. </script> 

    25. <html xmlns="http://www.w3.org/1999/xhtml" > 

    26. <head runat="server"> 

    27.         <title>無标題頁</title> 

    28. </head> 

    29. <body> 

    30.         <form id="form1" runat="server"> 

    31.         <div> 

    32.                 <asp:Button ID="btnOK" runat="server" OnClick="btnOK_Click" Text="送出" /></div> 

    33.         </form> 

    34. </body> 

    35. </html>

        頁面第一次運作的結果:

        按一下F5重新整理頁面的結果:

        點選一下“送出”按鈕之後的結果:

        由此可見每次打開一個頁面和重新整理一個頁面效果都是一樣的,隻有響應用戶端回發時IsPostBack屬性才是true。了解這個屬性和伺服器采用了一種機制來“記錄”伺服器控件的狀态這種做法(其實利用了ViewState和ControlState機制,這部分後續文章中會講到)對于将來資料綁定會有很大作用。

動态輸出javascript腳本

        對于Index.aspx頁面上面的執行情況,我們看到了滿意的結果。我們再來看一下這個頁面在用戶端生成的HTML代碼,在浏覽器視窗打開的頁面點滑鼠右鍵,然後選擇“檢視源檔案”,HTML代碼如下:

     1. 目前時間:2008-9-21 0:04:33響應用戶端回發而加載。 

     4. <head><title> 

     5.    無标題頁 

     6. </title></head> 

     8.         <form name="form1" method="post" action="Index.aspx" id="form1"> 

     9. <div> 

    10. <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTY3NzE5MjIyMGRkD2VvJFADDdEHh4W9UfAyzIvI3ss=" /> 

    11. </div> 

    12.         <div> 

    13.                 <input type="submit" name="btnOK" value="送出" id="btnOK" /></div> 

    14.            

    15. <div> 

    16.    <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgL8vZamCQLdkpmPAeM33vfm1ARVNKdKAoq5+eQdFI1J" /> 

    17. </div></form> 

    18. </body> 

    19. </html>

        我們會看到“目前時間:2008-9-21 0:04:33響應用戶端回發而加載。”這句話位于<html></html>标記之外。在第一夜時候就提到過,asp.net 頁面是滿足XML标準的HTML語言,但是通過在Page_Load事件中利用Response屬性會将文字輸出在<html></html>标記之外,不符合XHTML标準。這對于普通頁面來說也許并無大礙,但是如果在頻繁輸出 javascript腳本的網頁中,可能會對網頁的用戶端執行效果産生影響。因為javascript腳本塊在用戶端調用方法之前還是用戶端調用方法之後效果可能會不一樣。

        下面在Home窗體的Page_Load事件中添加代碼,如下:

    11. public partial class Home : System.Web.UI.Page 

    13.         protected void Page_Load(object sender, EventArgs e) 

    15.                 if (!Page.IsPostBack) 

    16.                 { 

    17.                         Response.Write("<script language='javascript'>alert('" + DateTime.Now.ToString() + "')</script>"); 

    18.                 } 

    19.         } 

    20. }

        這樣每次運作Home.aspx頁面的時候都會彈出一個對話框,如下圖:

        這不是我們所關心的,我們關注的是生成的HTML代碼,如下:

     1. <script language='javascript'>alert('2008-9-21 0:22:52')</script> 

     8.         <form name="form1" method="post" action="Home.aspx" id="form1"> 

    10. <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGTB6tgIyCoS2q3pZeKmhFwC24pQzw==" /> 

    13.            

    14.         </div> 

    15.         </form> 

    16. </body> 

    17. </html>

        可以看見輸出的javascript代碼在<html></html>标記之外。

        在Page類中有一個ClientScript屬性,它是ClientScriptManager的執行個體,這個類是在asp.net2.0中新增的。ClientScriptManager有如下幾個常用方法:

RegisterClientScriptBlock方法:向 Page 對象注冊用戶端腳本。

RegisterStartupScript方法:向 Page 對象注冊啟動腳本。

ClientScriptManager類通過鍵string和Type來唯一辨別腳本。具有相同類型的鍵和Type的腳本識為同一腳本。

        下面對Home窗體的Page_Load事件中輸入如下代碼:

    15.                 if (!ClientScript.IsClientScriptBlockRegistered(this.GetType(), "ClientScriptBlock")) 

    17.                         ClientScript.RegisterClientScriptBlock(this.GetType(), "ClientScriptBlock", "<script language='javascript'>alert('ClientScriptBlock')</script>"); 

    19.                 if (!ClientScript.IsStartupScriptRegistered(this.GetType(), "StartupScript")) 

    20.                 { 

    21.                         ClientScript.RegisterStartupScript(this.GetType(), "StartupScript", "<script language='javascript'>alert('StartupScript')</script>"); 

    22.                 } 

    23.                 //Response.Write("<script language='javascript'>alert('" + DateTime.Now.ToString() + "')</script>"); 

    24.         } 

    25. }

        執行該頁面時,會彈出兩個提示視窗,生成的HTML代碼如下:

     1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

     2. <html xmlns="http://www.w3.org/1999/xhtml" > 

     3. <head><title> 

     4.    無标題頁 

     5. </title></head> 

     6. <body> 

     7.         <form name="form1" method="post" action="Home.aspx" id="form1"> 

     8. <div> 

     9. <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGTB6tgIyCoS2q3pZeKmhFwC24pQzw==" /> 

    10. </div> 

    11. <script language='javascript'>alert('ClientScriptBlock')</script> 

    13.                    </div> 

    15. <script language='javascript'>alert('StartupScript')</script></form> 

        可以看出上面的兩個方法輸出的javascript腳本都在<form></form>标記之内,不會破環文章的結構,而且RegisterClientScriptBlock方法輸出的javascript腳本代碼塊靠近<form>标記的開始标記,而 RegisterStartupScript方法輸出的javascript腳本代碼塊靠近<form>标記的結束标記,了解這一點對于控制動态添加的用戶端腳本的時間是非常有利的。

        在asp.net中用戶端與伺服器端的互動預設都是整頁面送出,此時用戶端将目前頁面表單中的資料(包括一些自動生成的隐藏域)都送出到伺服器端,伺服器重新執行個體化一個目前頁面類的執行個體響應這個請求,然後将整個頁面的内容重新發送到用戶端,這種處理方式對運作結果沒什麼影響,不過這種方式加重了網絡的資料傳輸負擔、加大了伺服器的工作壓力,并且使用者還需要等待最終處理結果。假如是我們希望有這麼一個功能,當使用者填寫完使用者名之後就檢查伺服器資料庫裡是否已存在該使用者名,如果存在就給出已經存在此使用者名的提示,如果不存在就提示使用者此使用者名可用,對于這種情況其實隻需要傳遞一個使用者名作為參數即可,上面的做法卻需要送出整個表單,有點小題大做。解決上面的問題的辦法目前主流做法有三種:純javascript實作、微軟Ajax類庫實作還有用AjaxPro實作。後兩種做法在稍後的文章中會講到,這裡我講另外一種實作:通過回調技術。

        建立實作回調技術的網頁與普通asp.net網頁類似,隻不過還需要做以下特殊工作:

        (1)讓目前頁面實作 ICallbackEventHandler接口,這個接口定義了兩個方法:string GetCallbackResult ()方法和void RaiseCallbackEvent (string eventArgument)方法。其中GetCallbackResult ()方法的作用是傳回以控件為目标的回調事件的結果,RaiseCallbackEvent()方法的作用是處理以控件為目标的回調事件。

        (2) 為目前頁提供三個javascript用戶端腳本函數。一個javascript函數用于執行對伺服器的實際請求,在這個函數中可以提供一個字元串類型的參數發送到伺服器端;另一個javascript函數用于接收伺服器端方法的執行後傳回的字元串類型結果,并處理這個結果;還有一個是執行對伺服器請求的幫助函數,在伺服器代碼中通過GetCallbackEventReference()方法擷取這個方法的引用時由asp.net自動生成這個函數。

        下面我以一個詳細的例子來講述如何使用回調,用Dreamweaver建立一個Register. aspx頁面,代碼如下:

     1. <%@ Page Language="C#" ContentType="text/html" ResponseEncoding="gb2312" %> 

     2. <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %> 

     3. <%@ Import Namespace="System.Text" %> 

     4. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

     5. <html xmlns="http://www.w3.org/1999/xhtml"> 

     6. <head> 

     7. <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> 

     8. <title>使用者注冊</title> 

     9. <script language="javascript"> 

    10. //用戶端執行的方法 

    11. //下面的方法是接收并處理伺服器方法執行的傳回結果 

    12. function Success(args, context) 

    13. { 

    14.         message.innerText    = args; 

    15. } 

    16. //下面的方式是當接收伺服器方法處理的結果發生異常時調用的方法 

    17. function Error(args, context) 

    18. { 

    19.         message.innerText    = '發生了異常'; 

    20. } 

    21. </script> 

    22. <script language="c#" runat="server"> 

    23. string result=""; 

    24. // 定義在伺服器端運作的回調方法. 

    25. public void RaiseCallbackEvent(String eventArgument) 

    26. { 

    27.    if(eventArgument.ToLower().IndexOf("admin")!=-1) 

    28.    { 

    29.     result=eventArgument+"不能作為使用者名注冊。"; 

    30.    } 

    31.    else 

    32.    { 

    33.     result=eventArgument+"可以注冊。"; 

    34.    } 

    35.    //throw new Exception(); 

    36. } 

    37. //定義傳回回調方法執行結果的方法 

    38. public string GetCallbackResult() 

    39. { 

    40.    return result; 

    41. } 

    42. //伺服器上執行的方法 

    43. public void Page_Load(Object sender,EventArgs e) 

    44. { 

    45.    // 擷取目前頁的ClientScriptManager的引用 

    46.         ClientScriptManager csm = Page.ClientScript; 

    47.    // 擷取回調引用。會在用戶端生成WebForm_DoCallback方法,調用它來達到異步調用。這個方式是微軟寫的方法,會被發送到用戶端 

    48.    //注意這裡的"Success"和"Error"兩個字元串分别用戶端代碼中定義的兩個javascript函數 

    49.    //下面的方法最後一個參數的意義:true表示執行異步回調,false表示執行同步回調 

    50.    String reference = csm.GetCallbackEventReference(this, "args","Success","","Error",false); 

    51.    String callbackScript = "function CallServerMethod(args, context) {\n" +    

    52.     reference + ";\n }"; 

    53.    // 向目前頁面注冊javascript腳本代碼 

    54.    csm.RegisterClientScriptBlock(this.GetType(), "CallServerMethod",    

    55.     callbackScript, true); 

    56. } 

    57. </script> 

    58. </head> 

    59. <body> 

    60. <form id="form1" runat="server"> 

    61. <table border="1" cellpadding="0" cellspacing="0" width="400px"> 

    62. <tr> 

    63. <td width="100px">使用者名</td><td><input type="text" size="10" maxlength="20" id="txtUserName" onblur="CallServerMethod(txtUserName.value,null)" /><span id="message"></span></td> 

    64. </tr> 

    65. <tr> 

    66. <td>密碼</td><td><input type="password" size="10" maxlength="20" id="txtPwd" /></td> 

    67. </tr> 

    68. </table> 

    69. </form> 

    70. </body> 

    71. </html>

        上面的頁面中我已經添加了足夠詳盡的注視,不過我還是要說明幾點:

(1)

   1. <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>

這句表示目前頁面實作了ICallbackEventHandler接口,如果采用頁面與代碼分離的模式,背景cs代碼則應是:

   1. public partial class Register : System.Web.UI.Page, ICallbackEventHandler

   2. {

   3. //cs代碼

   4. }

(2)

   1. <input type="text" size="10" maxlength="20" id="txtUserName" onblur="CallServerMethod(txtUserName.value,null)" />

這裡有一個onblur="CallServerMethod(txtUserName.value,null),表示當使用者名文本框失去焦點之後激發CallServerMethod這個用戶端方法,這個用戶端方法是由asp.net動态生成的。

(3)

   1. csm.GetCallbackEventReference(this, "args","Success","","Error",false);

中的"Success"和"Error"分别代表用戶端的javascript函數,可以在代碼中見到,其中"Success"代表調用伺服器端方法成功後要執行的用戶端方法名,"Error"代表調用伺服器端方法失敗時調用的用戶端方法名。

該頁面在用戶端生成的HTML代碼如下:

     2. <html xmlns="http://www.w3.org/1999/xhtml"> 

     3. <head> 

     4. <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> 

     5. <title>使用者注冊</title> 

     6. <script language="javascript"> 

     7. //用戶端執行的方法 

     8. //下面的方法是接收并處理伺服器方法執行的傳回結果 

     9. function Success(args, context) 

    10. { 

    11.         message.innerText    = args; 

    12. } 

    13. //下面的方式是當接收伺服器方法處理的結果發生異常時調用的方法 

    14. function Error(args, context) 

    15. { 

    16.         message.innerText    = '發生了異常'; 

    17. } 

    18. </script> 

    19. </head> 

    20. <body> 

    21. <form name="form1" method="post" action="register.aspx" id="form1"> 

    22. <div> 

    23. <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> 

    24. <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" /> 

    25. <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA0MjMxNTU1OGRkIv6UMIqGy3vfPLfPRjEbuTwUrf8=" /> 

    26. </div> 

    27. <script type="text/javascript"> 

    28. <!-- 

    29. var theForm = document.forms['form1']; 

    30. if (!theForm) { 

    31.         theForm = document.form1; 

    32. } 

    33. function __doPostBack(eventTarget, eventArgument) { 

    34.         if (!theForm.onsubmit || (theForm.onsubmit() != false)) { 

    35.                 theForm.__EVENTTARGET.value = eventTarget; 

    36.                 theForm.__EVENTARGUMENT.value = eventArgument; 

    37.                 theForm.submit(); 

    38.         } 

    39. } 

    40. // --> 

    41. </script> 

    42. <script src="/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750" type="text/javascript"></script> 

    43. <script type="text/javascript"> 

    44. <!-- 

    45. function CallServerMethod(args, context) { 

    46. WebForm_DoCallback('__Page',args,Success,"",Error,false); 

    47.    }// --> 

    48. </script> 

    49. <table border="1" cellpadding="0" cellspacing="0" width="400px"> 

    50. <tr> 

    51. <td width="100px">使用者名</td><td><input type="text" size="10" maxlength="20" id="txtUserName" onblur="CallServerMethod(txtUserName.value,null)" /><span id="message"></span></td> 

    52. </tr> 

    53. <tr> 

    54. <td>密碼</td><td><input type="password" size="10" maxlength="20" id="txtPwd" /></td> 

    55. </tr> 

    56. </table> 

    57. <script type="text/javascript"> 

    58. <!-- 

    59. WebForm_InitCallback();// --> 

    60. </script> 

    61. </form> 

    62. </body> 

    63. </html>

在生成的HTML代碼中多了幾段javascipt教本塊,下面分别說明:

(1)第一部分

     1. <script type="text/javascript"> 

     2. <!-- 

     3. var theForm = document.forms['form1']; 

     4. if (!theForm) { 

     5.         theForm = document.form1; 

     6. } 

     7. function __doPostBack(eventTarget, eventArgument) { 

     8.         if (!theForm.onsubmit || (theForm.onsubmit() != false)) { 

     9.                 theForm.__EVENTTARGET.value = eventTarget; 

    10.                 theForm.__EVENTARGUMENT.value = eventArgument; 

    11.                 theForm.submit(); 

    13. } 

    14. // --> 

    15. </script>

這部分代碼是每個asp.net頁面發送到用戶端都會生成的,用于送出目前表單,其中eventTarget參數表示激發送出事件的控件,eventArgument參數表示發生該事件時的參數資訊。

(2)第二部分

   1. <script src="/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750" type="text/javascript"></script>

這部分代碼是用來生成一些用于Ajax調用的js腳本。說穿了,asp.net之是以開發起來友善,是因為微軟在幕後默默地為我們做了很多工作,回調的本質其實就是Ajax調用。

我們可以将“/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750”這部分拷貝到浏覽器位址欄中,如下圖:

回車之後會彈出一個下載下傳檔案對話框,如下圖:

将這個頁面儲存到本地,雖然預設的儲存檔案的字尾為“.axd”,但它其實是一個文本檔案,裡面是一些javascript代碼,我們可以用記事本打開,在裡面我們可以看到“WebForm_DoCallback”這個方法,如下:

在這個axd檔案裡做了很多幕後工作,是以我們的回調才相對比較簡單。

(3)第三部分

     3. function CallServerMethod(args, context) { 

     4. WebForm_DoCallback('__Page',args,Success,"",Error,false); 

     5.    }// --> 

     6. </script>

這部分代碼是背景生成的,通過擷取Page類的ClientScript屬性,也就是ClientScriptManager的執行個體注冊到頁面的,裡面定義了兩個javascript函數:CallServerMethod函數和WebForm_DoCallback函數,并且是在 CallServerMethod函數中調用WebForm_DoCallback函數。

(4)第四部分

     3. WebForm_InitCallback();// --> 

     4. </script>

        這部分代碼也是幕後生成的,這個javascript函數也可以在那個axd檔案中找到。如下圖:

        當我們在以除“admin”之外的字元串作為使用者名并移開焦點之後,會得到可以注冊的提示,如下圖:

        當我們輸入“admin”作為使用者名時的結果:

        另外,我們将伺服器端執行的方法做如下處理,也就是RaiseCallbackEvent(String eventArgument)這個方法,我們在這裡抛出一個異常,代碼如下:

     1. // 定義在伺服器端運作的回調方法. 

void RaiseCallbackEvent() void RaiseCallbackEvent(String eventArgument) 

     3. { 

     4.    /* 

     5.    if(eventArgument.ToLower().IndexOf("admin")!=-1) 

     6.    { 

     7.     result=eventArgument+"不能作為使用者名注冊。"; 

     8.    } 

     9.    else 

    10.    { 

    11.     result=eventArgument+"可以注冊。"; 

    12.    } 

    13.    */ 

    14.    throw new Exception(); 

    15. }

        再次運作,無論我們以什麼作為使用者名,都會得到如下結果:

        之是以會出現“發生了異常”這個字元串,是因為我們定義了function Error(args, context)這個javascript函數,并且把它作為調用伺服器端方法發生異常時的用戶端處理函數,它的處理方式就是顯示“發生了異常”這個字元串。

        寫完這篇文章已經是2008-9-26 日淩晨了,我發現這個系列的文章該叫《asp.net晨話》而不是叫《asp.net夜話》了,因為每次我寫完的時候都已經是淩晨了,呵呵。不過有這麼多朋友熱情的留言鼓勵,我的動力還是很大的。下一篇我已經基本完成了,不過需要我抽空再做一下文字校正工作。工作比較忙,時間特别不充足,希望大家多多諒解:)

本文轉自周金橋51CTO部落格,原文連結: http://blog.51cto.com/zhoufoxcn/166784,如需轉載請自行聯系原作者

繼續閱讀