asp.net夜话之五:Page类和回调技术
asp.net夜話之五:Page類和回調(diào)技術(shù)
在今天我主要要介紹的有如下知識(shí)點(diǎn):
Page類介紹
Page的生命周期
IsPostBack屬性
ClientScriptManager類
回調(diào)技術(shù)(CallBack)
?
Page類介紹
asp.net有時(shí)候也被成為WebForm,因?yàn)殚_(kāi)發(fā)一個(gè)asp.net頁(yè)面就像開(kāi)發(fā)一個(gè)WinFrom窗體一樣,我們同樣可以采用拖拽控件、雙擊產(chǎn)生相關(guān)處理代碼的方法。在asp.net中,創(chuàng)建一個(gè)頁(yè)面可以采用兩種模型。
單頁(yè)模型
用Dreamweaver創(chuàng)建的asp.net頁(yè)面就是單頁(yè)模型,當(dāng)然利用Visual Studio 2005也能創(chuàng)建單頁(yè)模型,不過(guò)在Visual Studio 2005中創(chuàng)建的頁(yè)面默認(rèn)不是單頁(yè)模型,要想在Visual Studio 2005創(chuàng)建單頁(yè)模型的網(wǎng)頁(yè)如下:
?
?
項(xiàng)處于未選中狀態(tài),默認(rèn)情況下這個(gè)選項(xiàng)是處于選中狀態(tài)的。這樣就創(chuàng)建了單頁(yè)模型的網(wǎng)頁(yè)。
此時(shí)的頁(yè)面代碼如下:
?
?
注意在頁(yè)面中有這樣一句代碼:
這句代碼與普通javascript語(yǔ)句塊不同的是有一個(gè)runat="server"屬性,表示這里的代碼是在服務(wù)器上運(yùn)行的C#代碼。切換到設(shè)計(jì)視圖,然后雙擊頁(yè)面,然后這部分會(huì)變成如下的樣子:
?
其中Page_Load就是頁(yè)面加載的時(shí)候在服務(wù)器上運(yùn)行的方法。
單頁(yè)模型的特點(diǎn)是HTML標(biāo)記、控件代碼及服務(wù)器端運(yùn)行的C#代碼全部包含在一個(gè)aspx頁(yè)面中,Web服務(wù)器第一次運(yùn)行該頁(yè)面的時(shí)候會(huì)將這個(gè)頁(yè)面生成一個(gè)類文件,對(duì)于上面的Index.aspx頁(yè)面,會(huì)生成ASP.Index_aspx的類,然后再將這個(gè)ASP.Index_aspx類編譯成IL代碼,Web服務(wù)器通過(guò)CLR(Common Language Runtime,通用語(yǔ)言運(yùn)行環(huán)境)運(yùn)行相應(yīng)的IL代碼。
單頁(yè)模型的缺點(diǎn)是頁(yè)面和代碼混在一起,維護(hù)起來(lái)較為麻煩。
?
代碼頁(yè)面分離模式
代碼頁(yè)面模式就是將頁(yè)的標(biāo)記(HTML代碼)和服務(wù)器端元素放在.aspx頁(yè)面中,而也代碼在位于一個(gè).aspx.cs中。采用默認(rèn)方式創(chuàng)建的aspx網(wǎng)頁(yè)就是這種方式。
下面就是一個(gè)采用代碼頁(yè)面分離模式創(chuàng)建的Home.aspx頁(yè)面的代碼:
?
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Home.aspx.cs" Inherits="Home" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>無(wú)標(biāo)題頁(yè)</title> </head> <body> <form id="form1" runat="server"> <div> </div> </form> </body> </html>?其對(duì)應(yīng)的頁(yè)代碼是:
?
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class Home : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } }?
首先要關(guān)注的aspx的頭部分代碼:
?
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Home.aspx.cs" Inherits="Home" %>?
@Page是一個(gè)頁(yè)面指令,在這里L(fēng)anguage="C#"指明了當(dāng)前頁(yè)面采用的后臺(tái)代碼是C#語(yǔ)言,CodeFile="Home.aspx.cs"表示這個(gè)頁(yè)面對(duì)應(yīng)的頁(yè)代碼文件是Home.aspx.cs這個(gè)文件,Inherits="Home"表示當(dāng)前aspx頁(yè)繼承自Home這個(gè)類。
現(xiàn)在再關(guān)注一下頁(yè)代碼文件聲明:
public?partial?class?Home?:?System.Web.UI.Page
?
從這部分代碼可以看出Home類是繼承自System.Web.UI.Page類的。注意這里還有一個(gè)C#2.0的關(guān)鍵字partial,這個(gè)關(guān)鍵字表示當(dāng)前代碼是一個(gè)局部類,以表示這個(gè)類是構(gòu)成整個(gè)Web頁(yè)面窗體的一部分。Web服務(wù)器運(yùn)行這個(gè)頁(yè)面的時(shí)候最終會(huì)將aspx頁(yè)面和對(duì)應(yīng)的頁(yè)代碼編譯成一個(gè)類文件,然后生成IL代碼。
代碼頁(yè)面分離模式的好處是頁(yè)面展示部分和邏輯控制部分的代碼分離開(kāi)來(lái),便于管理和維護(hù),這也是微軟推薦的開(kāi)發(fā)方式。
?
asp.net頁(yè)面的聲明周期
asp.net頁(yè)面運(yùn)行的時(shí)候?qū)⒔?jīng)歷一個(gè)聲明周期,這個(gè)生命周期中會(huì)進(jìn)行一系列的操作,調(diào)用一系列的方法。了解asp.net頁(yè)面的生命周期對(duì)于精確控制頁(yè)面的控件呈現(xiàn)方式和行為非常重要。
一般說(shuō)來(lái)一個(gè)常規(guī)頁(yè)面要經(jīng)歷如下幾個(gè)生命周期階段:
?
| 階段???????? | 說(shuō)明 |
| 頁(yè)請(qǐng)求 | 頁(yè)請(qǐng)求發(fā)生在頁(yè)生命周期開(kāi)始之前。用戶請(qǐng)求頁(yè)時(shí),ASP.NET 將確定是否需要分析和編譯頁(yè)(從而開(kāi)始頁(yè)的生命周期),或者是否可以在不運(yùn)行頁(yè)的情況下發(fā)送頁(yè)的緩存版本以進(jìn)行響應(yīng)。 |
| 開(kāi)始 | 在開(kāi)始階段,將設(shè)置頁(yè)屬性,如 Request 和 Response。在此階段,頁(yè)還將確定請(qǐng)求是回發(fā)請(qǐng)求還是新請(qǐng)求,并設(shè)置 IsPostBack 屬性。此外,在開(kāi)始階段期間,還將設(shè)置頁(yè)的 UICulture 屬性。 |
| 頁(yè)初始化 | 頁(yè)初始化期間,可以使用頁(yè)中的控件,并將設(shè)置每個(gè)控件的 UniqueID 屬性。此外,任何主題都將應(yīng)用于頁(yè)。如果當(dāng)前請(qǐng)求是回發(fā)請(qǐng)求,則回發(fā)數(shù)據(jù)尚未加載,并且控件屬性值尚未還原為視圖狀態(tài)中的值。 |
| 加載 | 加載期間,如果當(dāng)前請(qǐng)求是回發(fā)請(qǐng)求,則將使用從視圖狀態(tài)和控件狀態(tài)恢復(fù)的信息加載控件屬性。 |
| 驗(yàn)證 | 在驗(yàn)證期間,將調(diào)用所有驗(yàn)證程序控件的 Validate 方法,此方法將設(shè)置各個(gè)驗(yàn)證程序控件和頁(yè)的 IsValid 屬性。 |
| 回發(fā)事件處理 | 如果請(qǐng)求是回發(fā)請(qǐng)求,則將調(diào)用所有事件處理程序。 |
| 呈現(xiàn) | 在呈現(xiàn)期間,視圖狀態(tài)將被保存到頁(yè),然后頁(yè)將調(diào)用每個(gè)控件,以將其呈現(xiàn)的輸出提供給頁(yè)的 Response 屬性的 OutputStream。 |
| 卸載 | 完全呈現(xiàn)頁(yè)、將頁(yè)發(fā)送至客戶端并準(zhǔn)備丟棄時(shí),將調(diào)用卸載。此時(shí),將卸載頁(yè)屬性(如 Response 和 Request)并執(zhí)行清理。 |
?
在頁(yè)的生命周期中,一般會(huì)有如下事件:
?
?
| 頁(yè)事件 ? ??????????????? | 典型使用 |
| Page_PreInit | 使用 IsPostBack 屬性確定是否是第一次處理該頁(yè)。 創(chuàng)建或重新創(chuàng)建動(dòng)態(tài)控件。 動(dòng)態(tài)設(shè)置主控頁(yè)。 動(dòng)態(tài)設(shè)置 Theme 屬性。 讀取或設(shè)置配置文件屬性值。 注意:如果請(qǐng)求是回發(fā)請(qǐng)求,則控件的值尚未從視圖狀態(tài)還原。如果在此階段設(shè)置控件屬性,則其值可能會(huì)在下一階段被改寫(xiě)。 |
| Page_Init | 讀取或初始化控件屬性。 |
| Page_Load | 讀取和更新控件屬性。 |
| Control??events???? | 執(zhí)行特定于應(yīng)用程序的處理: 如果頁(yè)包含驗(yàn)證程序控件,請(qǐng)?jiān)趫?zhí)行任何處理之前檢查頁(yè)和各個(gè)驗(yàn)證控件的 IsValid 屬性。 處理特定事件,如 Button 控件的 Click 事件 |
| Page_PreRender | 對(duì)頁(yè)的內(nèi)容進(jìn)行最后更改。 |
| Page_Unload | 執(zhí)行最后的清理工作,可能包括: 關(guān)閉打開(kāi)的文件和數(shù)據(jù)庫(kù)連接。 完成日志記錄或其他特定于請(qǐng)求的任務(wù)。 |
?
需要注意的是,每個(gè)asp.net控件也有與asp.net類似的生命周期,如果aspx頁(yè)面中包含有asp.net服務(wù)器控件,那么在調(diào)用頁(yè)面的方法時(shí)也會(huì)調(diào)用控件的相關(guān)方法。
?
另外,Web應(yīng)用程序是無(wú)狀態(tài)的。每次請(qǐng)求一個(gè)新網(wǎng)頁(yè)或者刷新頁(yè)面服務(wù)器都會(huì)創(chuàng)建一個(gè)當(dāng)前頁(yè)的新實(shí)例,這就意味著無(wú)法獲取頁(yè)面的以前的信息,如果確實(shí)需要這么做,需要采用額外的機(jī)制。
我們將剛才新建的Index.aspx頁(yè)面中添加代碼,如下:
?
<%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> string date; protected void Page_Load(object sender, EventArgs e) { if (date == null)//如果date為空則設(shè)置為當(dāng)前時(shí)間的字符串形式 { date = DateTime.Now.ToString(); } Response.Write("當(dāng)前時(shí)間:"+date); } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>無(wú)標(biāo)題頁(yè)</title> </head> <body> <form id="form1" runat="server"> <div> </div> </form> </body> </html>??
???? 按照正常理解,第一次運(yùn)行的時(shí)候date字符串為null,會(huì)被設(shè)置成系統(tǒng)當(dāng)前的字符串表示形式,并且輸出,再次刷新的時(shí)候date字符串不再為空,會(huì)依然輸出剛才的時(shí)間字符串,但是結(jié)果卻不是這樣。第一次運(yùn)行的結(jié)果:
?
刷新頁(yè)面之后的結(jié)果:
?
?
這就證明了即使是刷新當(dāng)前頁(yè)也會(huì)重新生成一個(gè)當(dāng)前頁(yè)面的實(shí)例,因?yàn)橹挥性谏身?yè)面新實(shí)例的情況下date字符串變量才為空,才會(huì)被重新設(shè)置值。
?
IsPostBack屬性
Page類有一個(gè)IsPostBack屬性,這個(gè)屬性用來(lái)指示當(dāng)前頁(yè)面是第一次加載還是響應(yīng)了頁(yè)面上某個(gè)控件的服務(wù)器事件導(dǎo)致回發(fā)而加載。
這次我們繼續(xù)對(duì)Index.aspx頁(yè)面添加代碼,在頁(yè)面中增加了一個(gè)Button控件,如下:
?
<%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> string date; protected void Page_Load(object sender, EventArgs e) { if (date == null)//如果date為空則設(shè)置為當(dāng)前時(shí)間的字符串形式 { date = DateTime.Now.ToString(); } Response.Write("當(dāng)前時(shí)間:"+date); if (!Page.IsPostBack) { Response.Write("第一次加載。"); } else { Response.Write("響應(yīng)客戶端回發(fā)而加載。"); } } protected void btnOK_Click(object sender, EventArgs e) { } </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>無(wú)標(biāo)題頁(yè)</title> </head> <body> <form id="form1" runat="server"> <div> <asp:Button ID="btnOK" runat="server" OnClick="btnOK_Click" Text="提交" /></div> </form> </body> </html>?
?
頁(yè)面第一次運(yùn)行的結(jié)果:
?
按一下F5刷新頁(yè)面的結(jié)果:
?
?
點(diǎn)擊一下“提交”按鈕之后的結(jié)果:
?
?
?
由此可見(jiàn)每次打開(kāi)一個(gè)頁(yè)面和刷新一個(gè)頁(yè)面效果都是一樣的,只有響應(yīng)客戶端回發(fā)時(shí)IsPostBack屬性才是true。了解這個(gè)屬性和服務(wù)器采用了一種機(jī)制來(lái)“記錄”服務(wù)器控件的狀態(tài)這種做法(其實(shí)利用了ViewState和ControlState機(jī)制,這部分后續(xù)文章中會(huì)講到)對(duì)于將來(lái)數(shù)據(jù)綁定會(huì)有很大作用。
?
?
動(dòng)態(tài)輸出javascript腳本
對(duì)于Index.aspx頁(yè)面上面的執(zhí)行情況,我們看到了滿意的結(jié)果。我們?cè)賮?lái)看一下這個(gè)頁(yè)面在客戶端生成的HTML代碼,在瀏覽器窗口打開(kāi)的頁(yè)面點(diǎn)鼠標(biāo)右鍵,然后選擇“查看源文件”,HTML代碼如下:
?
當(dāng)前時(shí)間:2008-9-21 0:04:33響應(yīng)客戶端回發(fā)而加載。 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head><title> 無(wú)標(biāo)題頁(yè) </title></head> <body> <form name="form1" method="post" action="Index.aspx" id="form1"> <div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTY3NzE5MjIyMGRkD2VvJFADDdEHh4W9UfAyzIvI3ss=" /> </div> <div> <input type="submit" name="btnOK" value="提交" id="btnOK" /></div> <div> <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAgL8vZamCQLdkpmPAeM33vfm1ARVNKdKAoq5+eQdFI1J" /> </div></form> </body> </html>?
?
我們會(huì)看到“當(dāng)前時(shí)間:2008-9-21 0:04:33響應(yīng)客戶端回發(fā)而加載。”這句話位于<html></html>標(biāo)記之外。在第一夜時(shí)候就提到過(guò),asp.net頁(yè)面是滿足XML標(biāo)準(zhǔn)的HTML語(yǔ)言,但是通過(guò)在Page_Load事件中利用Response屬性會(huì)將文字輸出在<html></html>標(biāo)記之外,不符合XHTML標(biāo)準(zhǔn)。這對(duì)于普通頁(yè)面來(lái)說(shuō)也許并無(wú)大礙,但是如果在頻繁輸出javascript腳本的網(wǎng)頁(yè)中,可能會(huì)對(duì)網(wǎng)頁(yè)的客戶端執(zhí)行效果產(chǎn)生影響。因?yàn)閖avascript腳本塊在客戶端調(diào)用方法之前還是客戶端調(diào)用方法之后效果可能會(huì)不一樣。
下面在Home窗體的Page_Load事件中添加代碼,如下:
?
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class Home : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) { Response.Write("<script language='javascript'>alert('" + DateTime.Now.ToString() + "')</script>"); } } }?
?這樣每次運(yùn)行Home.aspx頁(yè)面的時(shí)候都會(huì)彈出一個(gè)對(duì)話框,如下圖:
?
?
這不是我們所關(guān)心的,我們關(guān)注的是生成的HTML代碼,如下:
?
<script language='javascript'>alert('2008-9-21 0:22:52')</script> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head><title> 無(wú)標(biāo)題頁(yè) </title></head> <body> <form name="form1" method="post" action="Home.aspx" id="form1"> <div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGTB6tgIyCoS2q3pZeKmhFwC24pQzw==" /> </div> <div> </div> </form> </body> </html>?
?
可以看見(jiàn)輸出的javascript代碼在<html></html>標(biāo)記之外。
在Page類中有一個(gè)ClientScript屬性,它是ClientScriptManager的實(shí)例,這個(gè)類是在asp.net2.0中新增的。ClientScriptManager有如下幾個(gè)常用方法:
RegisterClientScriptBlock方法:向 Page 對(duì)象注冊(cè)客戶端腳本。
RegisterStartupScript方法:向 Page 對(duì)象注冊(cè)啟動(dòng)腳本。
ClientScriptManager類通過(guò)鍵string和Type來(lái)唯一標(biāo)識(shí)腳本。具有相同類型的鍵和Type的腳本識(shí)為同一腳本。
下面對(duì)Home窗體的Page_Load事件中輸入如下代碼:
?
using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; public partial class Home : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { if (!ClientScript.IsClientScriptBlockRegistered(this.GetType(), "ClientScriptBlock")) { ClientScript.RegisterClientScriptBlock(this.GetType(), "ClientScriptBlock", "<script language='javascript'>alert('ClientScriptBlock')</script>"); } if (!ClientScript.IsStartupScriptRegistered(this.GetType(), "StartupScript")) { ClientScript.RegisterStartupScript(this.GetType(), "StartupScript", "<script language='javascript'>alert('StartupScript')</script>"); } //Response.Write("<script language='javascript'>alert('" + DateTime.Now.ToString() + "')</script>"); } }?
執(zhí)行該頁(yè)面時(shí),會(huì)彈出兩個(gè)提示窗口,生成的HTML代碼如下:
?
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head><title> 無(wú)標(biāo)題頁(yè) </title></head> <body> <form name="form1" method="post" action="Home.aspx" id="form1"> <div> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGTB6tgIyCoS2q3pZeKmhFwC24pQzw==" /> </div> <script language='javascript'>alert('ClientScriptBlock')</script> <div> </div> <script language='javascript'>alert('StartupScript')</script></form> </body> </html>?
可以看出上面的兩個(gè)方法輸出的javascript腳本都在<form></form>標(biāo)記之內(nèi),不會(huì)破環(huán)文章的結(jié)構(gòu),而且RegisterClientScriptBlock方法輸出的javascript腳本代碼塊靠近<form>標(biāo)記的開(kāi)始標(biāo)記,而RegisterStartupScript方法輸出的javascript腳本代碼塊靠近<form>標(biāo)記的結(jié)束標(biāo)記,了解這一點(diǎn)對(duì)于控制動(dòng)態(tài)添加的客戶端腳本的時(shí)間是非常有利的。
?
?
回調(diào)技術(shù)(CallBack)
?在asp.net中客戶端與服務(wù)器端的交互默認(rèn)都是整頁(yè)面提交,此時(shí)客戶端將當(dāng)前頁(yè)面表單中的數(shù)據(jù)(包括一些自動(dòng)生成的隱藏域)都提交到服務(wù)器端,服務(wù)器重新實(shí)例化一個(gè)當(dāng)前頁(yè)面類的實(shí)例響應(yīng)這個(gè)請(qǐng)求,然后將整個(gè)頁(yè)面的內(nèi)容重新發(fā)送到客戶端,這種處理方式對(duì)運(yùn)行結(jié)果沒(méi)什么影響,不過(guò)這種方式加重了網(wǎng)絡(luò)的數(shù)據(jù)傳輸負(fù)擔(dān)、加大了服務(wù)器的工作壓力,并且用戶還需要等待最終處理結(jié)果。假如是我們希望有這么一個(gè)功能,當(dāng)用戶填寫(xiě)完用戶名之后就檢查服務(wù)器數(shù)據(jù)庫(kù)里是否已存在該用戶名,如果存在就給出已經(jīng)存在此用戶名的提示,如果不存在就提示用戶此用戶名可用,對(duì)于這種情況其實(shí)只需要傳遞一個(gè)用戶名作為參數(shù)即可,上面的做法卻需要提交整個(gè)表單,有點(diǎn)小題大做。解決上面的問(wèn)題的辦法目前主流做法有三種:純javascript實(shí)現(xiàn)、微軟Ajax類庫(kù)實(shí)現(xiàn)還有用AjaxPro實(shí)現(xiàn)。后兩種做法在稍后的文章中會(huì)講到,這里我講另外一種實(shí)現(xiàn):通過(guò)回調(diào)技術(shù)。
?
?????? 創(chuàng)建實(shí)現(xiàn)回調(diào)技術(shù)的網(wǎng)頁(yè)與普通asp.net網(wǎng)頁(yè)類似,只不過(guò)還需要做以下特殊工作:
(1)讓當(dāng)前頁(yè)面實(shí)現(xiàn)ICallbackEventHandler接口,這個(gè)接口定義了兩個(gè)方法:string GetCallbackResult ()方法和void RaiseCallbackEvent (string eventArgument)方法。其中GetCallbackResult ()方法的作用是返回以控件為目標(biāo)的回調(diào)事件的結(jié)果,RaiseCallbackEvent()方法的作用是處理以控件為目標(biāo)的回調(diào)事件。
??
(2)為當(dāng)前頁(yè)提供三個(gè)javascript客戶端腳本函數(shù)。一個(gè)javascript函數(shù)用于執(zhí)行對(duì)服務(wù)器的實(shí)際請(qǐng)求,在這個(gè)函數(shù)中可以提供一個(gè)字符串類型的參數(shù)發(fā)送到服務(wù)器端;另一個(gè)javascript函數(shù)用于接收服務(wù)器端方法的執(zhí)行后返回的字符串類型結(jié)果,并處理這個(gè)結(jié)果;還有一個(gè)是執(zhí)行對(duì)服務(wù)器請(qǐng)求的幫助函數(shù),在服務(wù)器代碼中通過(guò)GetCallbackEventReference()方法獲取這個(gè)方法的引用時(shí)由asp.net自動(dòng)生成這個(gè)函數(shù)。
下面我以一個(gè)詳細(xì)的例子來(lái)講述如何使用回調(diào),用Dreamweaver創(chuàng)建一個(gè)Register. aspx頁(yè)面,代碼如下:?
?
?
<%@ Page Language="C#" ContentType="text/html" ResponseEncoding="gb2312" %> <%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %> <%@ Import Namespace="System.Text" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>用戶注冊(cè)</title> <script language="javascript"> //客戶端執(zhí)行的方法 //下面的方法是接收并處理服務(wù)器方法執(zhí)行的返回結(jié)果 function Success(args, context) { message.innerText = args; } //下面的方式是當(dāng)接收服務(wù)器方法處理的結(jié)果發(fā)生異常時(shí)調(diào)用的方法 function Error(args, context) { message.innerText = '發(fā)生了異常'; } </script> <script language="c#" runat="server"> string result=""; // 定義在服務(wù)器端運(yùn)行的回調(diào)方法. public void RaiseCallbackEvent(String eventArgument) { if(eventArgument.ToLower().IndexOf("admin")!=-1) { result=eventArgument+"不能作為用戶名注冊(cè)。"; } else { result=eventArgument+"可以注冊(cè)。"; } //throw new Exception(); } //定義返回回調(diào)方法執(zhí)行結(jié)果的方法 public string GetCallbackResult() { return result; } //服務(wù)器上執(zhí)行的方法 public void Page_Load(Object sender,EventArgs e) { // 獲取當(dāng)前頁(yè)的ClientScriptManager的引用 ClientScriptManager csm = Page.ClientScript; // 獲取回調(diào)引用。會(huì)在客戶端生成WebForm_DoCallback方法,調(diào)用它來(lái)達(dá)到異步調(diào)用。這個(gè)方式是微軟寫(xiě)的方法,會(huì)被發(fā)送到客戶端 //注意這里的"Success"和"Error"兩個(gè)字符串分別客戶端代碼中定義的兩個(gè)javascript函數(shù) //下面的方法最后一個(gè)參數(shù)的意義:true表示執(zhí)行異步回調(diào),false表示執(zhí)行同步回調(diào) String reference = csm.GetCallbackEventReference(this, "args","Success","","Error",false); String callbackScript = "function CallServerMethod(args, context) {\n" + reference + ";\n }"; // 向當(dāng)前頁(yè)面注冊(cè)javascript腳本代碼 csm.RegisterClientScriptBlock(this.GetType(), "CallServerMethod", callbackScript, true); } </script> </head> <body> <form id="form1" runat="server"> <table border="1" cellpadding="0" cellspacing="0" width="400px"> <tr> <td width="100px">用戶名</td><td><input type="text" size="10" maxlength="20" id="txtUserName" οnblur="CallServerMethod(txtUserName.value,null)" /><span id="message"></span></td> </tr> <tr> <td>密碼</td><td><input type="password" size="10" maxlength="20" id="txtPwd" /></td> </tr> </table> </form> </body> </html>?
?? 上面的頁(yè)面中我已經(jīng)添加了足夠詳盡的注視,不過(guò)我還是要說(shuō)明幾點(diǎn):
? (1)
?
<%@ Implements Interface="System.Web.UI.ICallbackEventHandler" %>?
這句表示當(dāng)前頁(yè)面實(shí)現(xiàn)了ICallbackEventHandler接口,如果采用頁(yè)面與代碼分離的模式,后臺(tái)cs代碼則應(yīng)是:
?
public partial class Register : System.Web.UI.Page, ICallbackEventHandler { //cs代碼 }?
(2)
(3)
中的"Success"和"Error"分別代表客戶端的javascript函數(shù),可以在代碼中見(jiàn)到,其中"Success"代表調(diào)用服務(wù)器端方法成功后要執(zhí)行的客戶端方法名,"Error"代表調(diào)用服務(wù)器端方法失敗時(shí)調(diào)用的客戶端方法名。
該頁(yè)面在客戶端生成的HTML代碼如下:
?
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=gb2312" /> <title>用戶注冊(cè)</title> <script language="javascript"> //客戶端執(zhí)行的方法 //下面的方法是接收并處理服務(wù)器方法執(zhí)行的返回結(jié)果 function Success(args, context) { message.innerText = args; } //下面的方式是當(dāng)接收服務(wù)器方法處理的結(jié)果發(fā)生異常時(shí)調(diào)用的方法 function Error(args, context) { message.innerText = '發(fā)生了異常'; } </script> </head> <body> <form name="form1" method="post" action="register.aspx" id="form1"> <div> <input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" /> <input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" /> <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMjA0MjMxNTU1OGRkIv6UMIqGy3vfPLfPRjEbuTwUrf8=" /> </div> <script type="text/javascript"> <!-- var theForm = document.forms['form1']; if (!theForm) { theForm = document.form1; } function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } } // --> </script> <script src="/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750" type="text/javascript"></script> <script type="text/javascript"> <!-- function CallServerMethod(args, context) { WebForm_DoCallback('__Page',args,Success,"",Error,false); }// --> </script> <table border="1" cellpadding="0" cellspacing="0" width="400px"> <tr> <td width="100px">用戶名</td><td><input type="text" size="10" maxlength="20" id="txtUserName" οnblur="CallServerMethod(txtUserName.value,null)" /><span id="message"></span></td> </tr> <tr> <td>密碼</td><td><input type="password" size="10" maxlength="20" id="txtPwd" /></td> </tr> </table> <script type="text/javascript"> <!-- WebForm_InitCallback();// --> </script> </form> </body> </html>?
在生成的HTML代碼中多了幾段javascipt教本塊,下面分別說(shuō)明:
(1)第一部分
?
<script type="text/javascript"> <!-- var theForm = document.forms['form1']; if (!theForm) { theForm = document.form1; } function __doPostBack(eventTarget, eventArgument) { if (!theForm.onsubmit || (theForm.onsubmit() != false)) { theForm.__EVENTTARGET.value = eventTarget; theForm.__EVENTARGUMENT.value = eventArgument; theForm.submit(); } } // --> </script>?
這部分代碼是每個(gè)asp.net頁(yè)面發(fā)送到客戶端都會(huì)生成的,用于提交當(dāng)前表單,其中eventTarget參數(shù)表示激發(fā)提交事件的控件,eventArgument參數(shù)表示發(fā)生該事件時(shí)的參數(shù)信息。
?
(2)第二部分
?
<script src="/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750" type="text/javascript"></script>
這部分代碼是用來(lái)生成一些用于Ajax調(diào)用的js腳本。說(shuō)穿了,asp.net之所以開(kāi)發(fā)起來(lái)方便,是因?yàn)槲④浽谀缓竽貫槲覀冏隽撕芏喙ぷ?#xff0c;回調(diào)的本質(zhì)其實(shí)就是Ajax調(diào)用。
我們可以將“/WebResource.axd?d=CcZ-_AaHZnD65xnNHEUijg2&t=633578466781093750”這部分拷貝到瀏覽器地址欄中,如下圖:
?
?
回車之后會(huì)彈出一個(gè)下載文件對(duì)話框,如下圖:
?
將這個(gè)頁(yè)面保存到本地,雖然默認(rèn)的保存文件的后綴為“.axd”,但它其實(shí)是一個(gè)文本文件,里面是一些javascript代碼,我們可以用記事本打開(kāi),在里面我們可以看到“WebForm_DoCallback”這個(gè)方法,如下:
?
?
在這個(gè)axd文件里做了很多幕后工作,所以我們的回調(diào)才相對(duì)比較簡(jiǎn)單。
?
(3)第三部分
?
<script type="text/javascript"> <!-- function CallServerMethod(args, context) { WebForm_DoCallback('__Page',args,Success,"",Error,false); }// --> </script>?
這部分代碼是后臺(tái)生成的,通過(guò)獲取Page類的ClientScript屬性,也就是ClientScriptManager的實(shí)例注冊(cè)到頁(yè)面的,里面定義了兩個(gè)javascript函數(shù):CallServerMethod函數(shù)和WebForm_DoCallback函數(shù),并且是在CallServerMethod函數(shù)中調(diào)用WebForm_DoCallback函數(shù)。
?
(4)第四部分
?
<script type="text/javascript"> <!-- WebForm_InitCallback();// --> </script>?
這部分代碼也是幕后生成的,這個(gè)javascript函數(shù)也可以在那個(gè)axd文件中找到。如下圖:
?
當(dāng)我們?cè)谝猿癮dmin”之外的字符串作為用戶名并移開(kāi)焦點(diǎn)之后,會(huì)得到可以注冊(cè)的提示,如下圖:
?
當(dāng)我們輸入“admin”作為用戶名時(shí)的結(jié)果:
?
另外,我們將服務(wù)器端執(zhí)行的方法做如下處理,也就是RaiseCallbackEvent(String eventArgument)這個(gè)方法,我們?cè)谶@里拋出一個(gè)異常,代碼如下:
?
// 定義在服務(wù)器端運(yùn)行的回調(diào)方法. public void RaiseCallbackEvent(String eventArgument) { /* if(eventArgument.ToLower().IndexOf("admin")!=-1) { result=eventArgument+"不能作為用戶名注冊(cè)。"; } else { result=eventArgument+"可以注冊(cè)。"; } */ throw new Exception(); }?
再次運(yùn)行,無(wú)論我們以什么作為用戶名,都會(huì)得到如下結(jié)果:
?
?
之所以會(huì)出現(xiàn)“發(fā)生了異常”這個(gè)字符串,是因?yàn)槲覀兌x了function Error(args, context)這個(gè)javascript函數(shù),并且把它作為調(diào)用服務(wù)器端方法發(fā)生異常時(shí)的客戶端處理函數(shù),它的處理方式就是顯示“發(fā)生了異常”這個(gè)字符串。
?
- 查看圖片附件
總結(jié)
以上是生活随笔為你收集整理的asp.net夜话之五:Page类和回调技术的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: .net后台获取html控件值的2种方法
- 下一篇: const 和 #define区别