ScriptManager 帮助您实现 Web 应用程序的 AJAX 功能
生活随笔
收集整理的這篇文章主要介紹了
ScriptManager 帮助您实现 Web 应用程序的 AJAX 功能
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文討論:
| 本文使用了以下技術: ASP.NET AJAX |
AJAX 和 ScriptManager
Web 服務和 ScriptManager
身份驗證和個性 化
工作原理
使用 ScriptManager 注冊對象
將 AJAX 置入 ASP.NET AJAX
研習腳本
當今,網站的使用者可以像發布者那樣,對網站的內容、發展方向及其成功承擔責任。每天都會涌現出許多樣式新穎的網站,如社交站點、博客、在線相冊集和 Wiki,這只是其中的一部分,而這種趨勢才剛剛開始。雖然您的網站可以提供最好的內容,但是,如果不能讓用戶參與網站的發展與成長,您的網站很快就會毫 無用武之地。 作 為一名開發人員,您的任務是使用便利的工具來滿足普通使用者的需求。要使一個站點更加引人注目,從 Web 瀏覽器本身入手是再自然不過的了,因為它是站點中最貼近使用者的部分。遺憾的是,當前 Web 瀏覽器中的許多功能并不總是那么容易利用。編寫與瀏覽器交互的代碼必須付出巨大的艱辛,因為存在著不計其數的瀏覽器與操作系統組合,缺乏一致性的問題令人 傷透腦筋。若能采用單一平臺并行地構建富客戶端和 Web 應用程序,那將會十分有益,這樣一來,針對某一瀏覽器環境而完善最終用戶體驗所付出的勞動就不需要針對另一瀏覽器環境而重復進行。 Microsoft 發布的 ASP.NET AJAX 可以滿足這種 Web 應用程序開發中的需求。本文旨在擴展您對 ASP.NET AJAX 的中心組件(名為 ScriptManager 控件)的認識,并展示如何使用它實現 ASP.NET AJAX 高級編程。ScriptManager 是放置在 Web 窗體上的服務器端控件,在 ASP.NET AJAX 中發揮核心作用。其主要任務是調解 Web 窗體上的所有其他 ASP.NET AJAX 控件,并將適當的腳本庫添加到 Web 瀏覽器中,從而使 ASP.NET AJAX 的客戶端部分能夠正常工作。您經常會發現自己使用 ScriptManager 注冊其他控件、Web 服務和客戶端腳本。 作 為服務器端的控件,ScriptManager 回應 ASP.NET 頁面生命周期中的事件,利用這些事件協調 ASP.NET AJAX 使用的所有控件、選項和代碼的活動。ScriptManager 將關聯某一特定事件,當事件發生時獲得通知,并根據環境配置多個設置;此過程將通過 ASP.NET 頁面的呈現循環多次重復進行。不過,它所配置的設置往往正是您無縫使用 ASP.NET AJAX 所需的設置。 首 先,我們將了解 ScriptManager 控件可幫助您實現的 ASP.NET AJAX 的主要功能,然后開始探討該控件在服務器上的生命周期。通過了解 ScriptManager 的內部結構,您會對該控件為 Web 應用程序開發提供的選項有更深入的認識,并了解如何從中獲得最大好處。 讓 我們從腳本開始,因為它是 ASP.NET AJAX 的中心元素。實際上,ASP.NET AJAX 的所有功能均依賴其腳本庫。我們隨后將瀏覽 ASP.NET AJAX 中對 AJAX 支持的某些功能,與 Web 服務的交互方式,最后談一談有關身份驗證的問題。在對各個問題的討論過程中,還將向您展示如何通過 ScriptManager 對選項進行調整。
使用 ScriptManager 編制腳本 圖 1 中的代碼塊演示了在 ASP.NET AJAX 中定義類的標準方法。客戶端腳本庫的內部結構不在本文討論范圍內,但總體來說,創建一個基于 ASP.NET AJAX 腳本擴展的類,其常見的必要步驟如下:
{
protected void Page_Load(object sender, EventArgs e)
{
this.MyCustomButton.AlertMessage = "Hey, stop clicking me!";
}
}
通 過在瀏覽器中使用如下腳本,還可以直觀地與該控件實例進行交互: <script type="text/javascript">
function get_text()
{
var mb = $find("MyCustomButton");
var text = mb.get_AlertMessage();
// do something with the text
}
</script>
請 注意,我使用同一個名稱 MyCustomButton 來引用服務器端和客戶端的控件;我還與屬性 AlertMessage 進行交互,就如同該值無縫地跨越服務器/客戶端的邊界。這讓人感到十分自然,但在 ASP.NET AJAX 之前,這種統一的服務器/客戶端編程模型很難使用,而且要求使用大量的自定義代碼。如今,在 ASP.NET AJAX 中,已集成并完全支持這種統一的服務器/客戶端模式。 這 種服務器/客戶端緊密集成的奇妙功能源于兩個新接口,即 IScriptControl 和 IExtenderControl。要使用這些接口,首先通過繼承的方式定義您的 ASP.NET Web 控件,實現所需的接口方法,然后通過頁面的 ScriptManager 控件來注冊控件。例如,下面的服務器端控件代碼框架實現了 IScriptControl 所需的方法: class CustomButton : Button, IScriptControl
{
IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
{
...
}
IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
{
...
}
}
GetScriptDescriptors 和 GetScriptReferences 方法將向 ScriptManager 控件返回所有必需的信息,以便從邏輯上將您的控件代碼表示為服務器和客戶端對象。最終結果就如同您之前看到的那樣,對象實例將跨越服務器/客戶端邊界。 GetScriptReferences 將返回您的控件代碼所需的腳本文件列表。在它必須返回的一個腳本文件中包含將您的控件定義為腳本類(即服務器端控件的客戶端腳本版本)的內容。 另 一方面,GetScriptDescriptors 返回稱為 ScriptDescriptors 的信息或描述客戶端類的屬性和事件的對象。可將 ScriptDescriptors 看作持有客戶端類的元數據,其中包括屬性及其關聯值。 圖 2 顯示了前面概述的服務器控件的更為完整的示例。在圖中,您可以看到已填寫完的 GetScriptDescriptors 和 GetScriptReferences 方法的主體。 ?Figure?2?使用 GetScriptDescriptors 和 GetScriptReferences private string _alertMessage = null;
public string AlertMessage
{
get { return _alertMessage; } set { alertMessage = value; }
}
public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new ScriptControlDescriptor(
"CustomButton", this.ClientID);
descriptor.AddProperty("AlertMessage", this._alertMessage);
return new ScriptDescriptor[] { descriptor };
}
public IEnumerable<ScriptReference> GetScriptReferences()
{
ScriptReference reference = new ScriptReference(
"MyCustomContent.JScript1.js", "MyCustomContent");
return new ScriptReference[] { reference };
}
在 GetScriptDescriptors 的主體中,我實例化了一個 ScriptDescriptor 對象。ScriptDescriptor 的構造函數使用了我當前所述的 CustomButton 控件的名稱。我隨后向 ScriptDescriptor 中添加了 AlertMessage 屬性和它的值(由私有成員 _alertMessage 持有)。現在,我希望從客戶端代碼中與之交互的屬性 AlertMessage 已經在 ASP.NET AJAX 中得到了描述。 在 GetScriptReferences 的主體中,需要注意的是我要創建并返回一個指向 .js 文件的 ScriptReference 對象。此 .js 文件保存著該控件的客戶端代碼,因而保存著此控件的所有客戶端功能。 現 在,我可以使用 ScriptManager 注冊此控件,使之意識到此控件的存在(稍候我們再探討這一步驟)。當呈現該頁面時,ScriptManager 控件將對它得到通知的各種事件做出回應,并調用 GetScriptDescriptors 和 GetScriptReferences。隨后,這兩個方法會向 ScriptManager 控件返回包含所有必需信息的 ScriptDescriptors 和 ScriptReferences,以便將客戶端對象與相應的服務器對象關聯起來。本例中,ScriptDescriptor 描述了一個名為 CustomButton 的自定義控件,該控件具有 AlertMessage 屬性。ScriptDescriptor 中還包含在 GetScriptDescriptors 中設置的屬性的值。 ScriptManager 控件還將獲得一個對名為 MyCustomContent.JSScript1.js 的外部 .js 文件的引用。對腳本文件的引用將采取從 GetScriptReferences 返回的 ScriptReference 對象的形式。對 .js 文件中控件的客戶端類的原型和腳注的定義如圖 3 所示。 ?Figure?3?Client 控件的原型 CustomButton.prototype = {
initialize : function() {
// initialize the base
CustomButton.callBaseMethod(this,'initialize');
this._onclick =
Function.createDelegate(this, this._onclick);
Sys.UI.DomEvent.addHandler(
this.get_element(), 'click', this._onclick);
},
dispose : function() {
// release the handlers
$clearHandlers(this.get_element());
// call to the base to do its dispose
CustomButton.callBaseMethod(this,'dispose');
},
get_AlertMessage: function(){
return this._alertMessage;
},
set_AlertMessage: function(value){
this._alertMessage = value;
return;
},
_onclick: function(e){
alert(this._alertMessage);
return;
}
}
CustomButton.registerClass("CustomButton",Sys.UI.Control);
if(typeof(Sys)!=='undefined') Sys.Application.notifyScriptLoaded();
首 先應該注意的是,此客戶端類對于 AlertMessage 屬性具有 get_ 和 set_ 方法。ScriptManager 控件可將該屬性值設置為您在服務器上通過 ScriptDescriptor 對象指定的任何值。因為我在服務器代碼中公開了 AlertMessage,那么,無論該屬性在服務器上設為何值,現在都會在客戶端對象所公開的 AlertMessage 屬性中得到反映。
AJAX 和 ScriptManager 許 多開發人員在首次使用 ASP.NET AJAX 時都會從 UpdatePanel 控件開始。如果該頁面中包含一個 ScriptManager 控件,并且 UpdatePanel 包含了任何控件,那么,通過 AJAX 提供的便利功能,可對 UpdatePanel 中的控件進行異步更新。在 Visual Studio? 的設計器圖面上,設置通常與圖 4 類似。 圖 4?一個簡單的異步控件? 這 確實是最基本的了。如果有人在該設置中單擊“Button”,則 Button 控件將引發一個回發事件,該事件將由 UpdatePanel 控件捕獲。然后,UpdatePanel 會將該回發事件作為一個部分回發而重新提交,其內容將得到異步更新(瀏覽器無需完全重新加載頁面)。 然 而,可能會出現許多有趣的情形,您的期望將會落空,并給您留下一片混亂。例如,使用 UpdatePanel 控件和 AJAX 會造成一種分離腳本引用的離奇的新方式。在過去,由于出現完全回發,您可能向頁面中添加了如下腳本: protected void Button1_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterStartupScript(
this.GetType(),"myscript","alert('hello world!');");
}
但 通過 ASP.NET AJAX,此新方法卻可以分離腳本引用。這是為什么?因為 ClientScriptManager 是不會響應部分回發和部分呈現的控件,因此,用它注冊的腳本代碼不會包含在部分回發的頁面響應中。 相 反,如果您希望該按鈕將客戶端腳本引入到頁面輸出中,則可以運用 ScriptManager: protected void Button1_Click(object sender, EventArgs e)
{
ScriptManager.RegisterStartupScript(
this,this.GetType(),"myscript","alert('hello world!');",true);
}
結 果是相同的,但方法略有差別。實際上,ClientScriptManager 控件的方法現在已作為靜態方法(如 RegisterStartupScript)包含在 ScriptManager 控件中。現在,每當您利用 ASP.NET AJAX 并想通過部分回發使用腳本時,都必須改用 ScriptManager 控件公開的方法。 作 為另一種高級方案,請考慮圖 5 中的控件設置。在正常情況下,一旦單擊 LinkButton,就會引起 Web 窗體的完全回發。但是,如何才能做到單擊 LinkButton 而使它異步刷新 UpdatePanel?換句話說,您希望單擊 LinkButton 而使 UpdatePanel 表現得如同 LinkButton 在其內部一樣。要做到這一點,請使用 ScriptManager 控件上名為 RegisterAsyncPostBackControl 的方法: 圖 5?異步使用 Link-Button? protected void Page_Load(object sender, EventArgs e)
{
// register the LinkButton1 control as one that can
// cause partial postbacks.
ScriptManager1.RegisterAsyncPostBackControl(LinkButton1);
}
現 在,單擊 LinkButton 將引起 UpdatePanel 異步刷新,就好像 LinkButton 控件真的在 UpdatePanel 中一樣。 作 為一個相反行為的例子,您也可使一個 UpdatePanel 中的元素引起整個頁面刷新,即完全回發。要做到這一點,只需使用 ScriptManager 上另一個名為 RegisterPostBackControl 的方法: protected void Page_Load(object sender, EventArgs e)
{
// Button1 will cause a full post-back no matter
// where it is on the page.
ScriptManager1.RegisterPostBackControl(Button1);
}
此 代碼將引起 Button1 控件對頁面執行完全回發,即使 Button1 位于 UpdatePanel 的內部。 現 在讓我們進一步深入討論。您已經了解如何使用 ScriptManager 調整觸發 UpdatePanel 控件的部分回發事件的控件,但是,當 UpdatePanel 刷新時,如何才能對頁面上某一位置的控件(完全在 UpdatePanel 之外)進行更新?這同樣可以通過 ScriptManager 控件實現。 通 過 ScriptManager 的 RegisterDataItem 方法,可以很輕松地刷新 UpdatePanel 之外的控件或數據。RegisterDataItem 允許在 UpdatePanel 控件回發時將您選擇的額外數據發送到客戶端;該數據可供您對客戶端編寫的腳本使用。 例 如,您遇到一種與圖 6 中的控件類似的情形。在這個例子中,我想用 Calendar 控件中被單擊的任何值更新 Label。這看起來很簡單,但 Calendar 控件在 UpdatePanel 內部,而 Label 不在 UpdatePanel 內部。如何才能讓 UpdatePanel 刷新該 Label?答案很簡單:如果我在服務器端代碼中使用 RegisterDataItem,則可以將額外數據發送到我的客戶端代碼。客戶端可以使用從 RegisterDataItem 發送的數據,并用它刷新 Label: 圖 6?更新 UpdatePanel 之外的控件? protected void Calendar1_SelectionChanged(object sender, EventArgs e)
{
ScriptManager1.RegisterDataItem(
Label1, Calendar1.SelectedDate.ToShortDateString());
}
RegisterDataItem 將您打算更新的控件作為第一個參數,將您希望用來更新控件的原始數據作為第二個參數。ScriptManager 控件接受您傳遞的數據,將其打包,然后將它作為對部分回發事件的響應的一部分而發送給客戶端。在客戶端代碼中,您可以在事件完成后檢索從 ScriptManager 控件發送的數據,如下所示: <script type="text/javascript">
Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(
PageLoadingHandler);
function PageLoadingHandler(sender,args){
var dataItems = args.get_dataItems();
if($get('Label1')!==null){
$get('Label1').innerHTML = dataItems['Label1'];
}
return;
}
</script>
請 看一下腳本代碼。該腳本可完成多項工作。首先,它通過 PageRequestManager 客戶端類注冊了 pageLoading 事件。接著,它為 pageLoading 事件實現了事件處理程序 PageLoadingHandler。它從第二個參數 args 中獲取了數據項的名稱/值集合。最后,它使用您為服務器上的 RegisterDataItem 提供的用作第一個參數的控件名稱檢索所需的值。
Web 服務和 ScriptManager 現 在,您可以輕松地從 ASP.NET AJAX 中的腳本以異步方式與 Web 服務進行交互,處理響應(包括錯誤),從而獲得強大的能力以使您的頁面真正達到用途廣泛、出類拔萃。從點到點(瀏覽器到服務器),通過 ASP.NET AJAX,以一種最直觀的方式利用當前最新的 Web 技術。 對 于 ASP.NET AJAX 應用程序中所需的各種服務,ScriptManager 控件起到全局注冊器的作用。圖 7 顯示了通過 Visual Studio 所見的 ScriptManager 控件的屬性菜單。 圖 7?ScriptManager 屬性? 您 可以看到,我突出顯示了 Scripts 集合,在其下方還有一個 Services(Web 服務)集合。您需要從您的客戶端代碼中用 ScriptManager 注冊任何想要與之交互的服務。要向 ScriptManager 控件中添加服務引用,只需展開 Services 集合并添加引用,如圖 8 所示。 圖 8?添加 Web 服務引用?(單擊該圖像獲 得較大視圖) 這 樣做到底做有什么作用?稍后我會進行詳細解釋,不過,如果您希望通過 ScriptManager 查看引用某個服務的頁面的源文件,可能會發現其內容中含有與以下類似的代碼: <script src="WebService.asmx/jsdebug" type="text/javascript"></script>
此 外部引用由 ScriptManager 添加到頁面輸出,因為 Web 服務已向其注冊。請注意,Web 服務的名稱后面附加了 /jsdebug;如果這是一個發布版,就會只在名稱后面附加 /js。當 ASP.NET AJAX 管道發現對此服務的請求,且請求后面附帶 /js 時,就會返回一個包裝 Web 服務方法的腳本類(稱為代理腳本)。該代理腳本類的每個方法的主體都將執行異步調用相應的 Web 服務方法。因此,要調用 Web 服務方法,只需調用 ASP.NET AJAX Web 服務框架為您構建的腳本類上的相應方法即可。就是如此簡單。 例 如,一個名為 WebService 的服務公開了一個名為 Add 的方法,如下所示 [WebMethod]
public int Add(int a, int b) { return a + b; }
可 以從此腳本中調用該方法: function CallAdd()
{
// method will return immediately
// processing done asynchronously
WebService.Add(0,6, OnMethodSucceeded, OnMethodFailed);
}
在 本例中,由框架送交給您的自動生成的腳本代理類包裝了 Web 服務方法調用,這樣您便可以從腳本中與之交互。因此,WebService.Add 會調用相應的 Web 服務方法 Add。 在 調用某個 Web 服務時,通常定義兩個回調:一個用于成功的情況,另一個用于失敗的情況。由于 Web 服務調用是異步執行的,所以必須通過回調來了解調用的實際完成情況。例如,下面的方法分別實現成功回調和失敗回調: function OnMethodSucceeded(result, eventArgs)
{
var label = Sys.UI.DomElement.getElementById('Label1');
var sb = new Sys.StringBuilder();
sb.append('You got back a value of ');
sb.append(result.toString());
label.innerHTML = sb.toString();
}
function OnMethodFailed(error)
{
alert(error.get_message());
}
從 Web 服務中以異步方式發送請求和接收結果,因此最終結果與使用 UpdatePanels 控件類似。使用從 Web 服務接收的數據即可對內容進行刷新,無需完全的瀏覽器回發。在前面所示的示例代碼中,我通過 Web 服務調用 Web 方法 Add 的結果對 Label1 進行了異步更新。最終結果可能如圖 9 所示。不會發生任何完全回發,最終結果絕對是無縫的。 圖 9?更新后的 Label?
身份驗證和個性化 ASP.NET AJAX 的高級功能(比如身份驗證和個性化)同樣使用 Web 服務。ASP.NET AJAX 中的身份驗證服務實現了兩種方法:一種用于用戶登錄,另一種用于用戶注銷: Sys.Services.AuthenticationService.login
Sys.Services.AuthenticationService.logout
ASP.NET AJAX 使用表單身份驗證,這就是說,當一個有效的表單身份驗證 cookie 被 Web 服務器插入到瀏覽器的會話中時,用戶就算登錄了。ASP.NET AJAX 中 cookie 數據插入到會話中的方式與通過完全回發所用的方式相同,因此,實際上它們所采用的機制沒有任何區別。一旦將 cookie 插入到瀏覽器的會話中,客戶端就通過了服務器的身份驗證,從而可以查看受限的頁面和內容。 能 夠從客戶端腳本對特定用戶進行身份驗證并完成登錄或注銷,這對基于交互性很高的用戶的系統來說,產生的結果可能會非常有意義。通過 AuthenticationService 和腳本實現用戶登錄的例子如下所示: <script type="text/javascript">
function MyMethod(username, password)
{
Sys.Services.AuthenticationService.login(username,
password,false,null,null,null,null,"User Context");
}
</script>
在 AJAX 應用程序中啟用身份驗證的步驟具有完備的文檔可查(參見 ajax.asp.net),所以,此處無需贅述。不過,您可以清楚地看到,身份驗證一經啟用,就可通過腳本輕松地實現表單身份驗證。 如 果標準的 ASP.NET 身份驗證服務不符合需求,您可以自行創建服務并通過 ScriptManager 控件進行注冊。Web 服務所需的實現身份驗證功能的方法如下所示: [WebMethod]
public bool Login(string userName,
string password, bool createPersistentCookie)
{
... // code to check user credentials and log user in
}
[WebMethod]
public void Logout()
{
... // code to log user out.
}
在 填寫完您的實現代碼之后,必須使用 ScriptManager 控件注冊您的新身份驗證服務。在注冊了新身份驗證服務之后,您以前在客戶端使用的與 ASP.NET AJAX 默認身份驗證服務進行交互的任何代碼現在都將改用您的服務。因此,在客戶端腳本這邊,無需做任何更改就可使用您自行定義的身份驗證 Web 服務。 以 下是通過 ScriptManager 以聲明的方式注冊一個身份驗證服務的示例: <asp:ScriptManager ID="ScriptManager1"
runat="server" >
<AuthenticationService
Path="WebService.asmx" />
</asp:ScriptManager>
此 時,您還可以在 ASP.NET AJAX 中利用您自己的配置文件服務,而無需具體實現該服務。配置文件服務可通過 ScriptManager 控件以相同方式注冊為一個身份驗證服務。配置文件服務使您能夠針對特定的用戶定制一個網站。由于這種定制是通過客戶端腳本完成的,因此用戶看到的結果將是 直觀和無縫的。通過 ajax.asp.net 可獲得多個示例。
工作原理 ScriptManager 控件的存在有兩個基本階段。在第一個階段,它確認環境可以支持 ASP.NET AJAX 的所有豐富的功能,并為支持這些功能完成所有必要的工作。在第二個階段,它通過客戶端上運行的代碼真正執行異步通信,從而使腳本可以完成必要的頁面更新。 因為它是服務器端控件,而 ASP.NET 中的 Web 編程是事件驅動的,所以,ScriptManager 控件的核心在于它如何注冊和響應事件。圖 10 顯示了 ScriptManager 控件響應的事件的概述。 圖 10?在 ASP.NET AJAX 中定義類?
使用 ScriptManager 注冊對象 可 以使用 ScriptManager 以聲明的方式添加腳本和服務引用。不過,您也可以通過 ScriptManager 控件上的 Services 和 Scripts 屬性以編程方式添加它們。您還可以選擇要發送到客戶端的特定數據,而且可以設計控件,使其作為特殊的腳本控件與 ScriptManager 配合使用。所有這些功能均要求您使用 ScriptManager 控件引用您的對象。 那 么 ScriptManager 如何處理所有這些引用呢?當瀏覽器首次請求一個承載 ScriptManager 控件和 ASP.NET AJAX 功能的頁面時,頁面子控件的初始化階段將會調用 ScriptManager 的 OnInit 方法。OnInit 完成若干重要步驟,包括檢查是否只有一個 ScriptManager 控件駐留在頁面上,以及頁面當前是否正在進行部分回發。不過,OnInit 執行的最重要的步驟是注冊一系列由宿主頁面生成的事件,如 InitComplete、PreRenderComplete 和 PreRender。ScriptManager 控件需要知道這些事件在其父頁面上的發生時間,因為 ASP.NET AJAX 通過利用這些頁面事件而進行工作。 將 腳本和服務引用加載到瀏覽器中的最重要的頁面事件是頁面的 PreRenderComplete 事件。該事件的處理程序叫做 OnPagePreRenderComplete(參見圖 11),它是 ScriptManager 控件本身的一個方法。 ?Figure?11?ScriptManager 的 OnPagePreRenderComplete private void OnPagePreRenderComplete(object sender, EventArgs e)
{
if (!IsInAsyncPostBack)
{
if (SupportsPartialRendering)
{
IPage.ClientScript.GetPostBackEventReference(
new PostBackOptions(this, null, null, false,
false, false, false, true, null));
}
RegisterGlobalizationScriptBlock();
RegisterScripts();
RegisterServices();
}
else RegisterScripts();
}
OnPagePreRenderComplete 負責處理通過 ScriptManager 控件注冊到頁面的 PreRenderComplete 事件的所有腳本和服務(包括腳本控件所需的腳本、ScriptManagerProxy 控件引用的腳本等)。如果頁面沒有進行部分回發,則會在注冊所有腳本和服務的同時注冊一個全局化腳本塊。另一方面,如果進行部分回發,則只注冊腳本。需要 為部分回發和完全回發注冊腳本,因為 ScriptManager 控件提供隨時包含腳本的功能。 您 的所有腳本和服務都在這個階段注冊,但這意味著什么?腳本或引用的注冊如何轉化為頁面上的某種輸出? 請 記住,您仍在處理第一個頁面請求,所以,您并未處理部分回發。正因為如此,您仍可以使用 ClientScriptManager 來注冊腳本。例如,RegisterScripts 遍歷每個使用 ScriptManager 控件注冊的腳本,然后將其轉交給頁面的默認 ClientScriptManager 實例。當呈現頁面時,將會調用 ClientScriptManager,腳本引用將被添加到頁面輸出,就像 ASP.NET AJAX 未出現之前的處理方式一樣。 在 異步回發過程中還會調用 RegisterScripts(由于圖 11 中所示的 else 子句)。該方法仍然會調用 ClientScriptManager,但是,因為 ClientScriptManager 不知道如何處理異步回發,不會發生任何操作。RegisterScripts 會轉而調用內部方法 RegisterScriptIncludeInternal,該方法會將腳本引用收藏在一個內部數組中,供以后使用。 如 何處理 Web 服務引用?我們知道,通過將 Web 服務引用添加到 ScriptManager 控件,頁面中就會添加對腳本的引用。該腳本引用會導致瀏覽器發出一個對特定 URL 的請求。ASP.NET AJAX 框架將檢查實現 Web 服務的類,并返回一個可在客戶端代碼中使用的代理腳本類。瀏覽器會下載這個自動生成的代理類,這將成為您的 Web 服務引用。 為 了將腳本引用自動生成到代理類,RegisterScripts 將遍歷您擁有的每個 Web 服務引用,最后調用下列方法: private string GetProxyPath(Control containingControl, bool debug)
{
if (debug)
return GetServicePath(containingControl, true) +
RestHandlerFactory.ClientDebugProxyRequestPathInfo;
else
return GetServicePath(containingControl, true) +
RestHandlerFactory.ClientProxyRequestPathInfo;
}
由 GetProxyPath 生成的腳本引用將按照與常規腳本引用相同的方式添加到頁面中:通過利用 ClientScriptManager 處理第一次頁面請求以及將引用“收藏”到某個內部數組中。 這 個內部數組是什么?實際上它是多個數組。回想一下,對于第一個頁面請求,ScriptManager 依賴傳統的方法,只是將腳本和服務引用添加到頁面的 ClientScriptManager 類。但在部分回發過程中,使用 ClientScriptManager 不會起作用。ScriptManager 必須構造其他的替代方法,這就是使用內部數組的緣由。您將會看到,對部分回發的響應實際上是格式良好、易于解析的數據塊,客戶端框架將對其進行檢查和使 用。ScriptManager 控件的 Render 事件將會被調用,接著,該事件會調用另一個類 PageRequestManager 的成員方法 ProcessScriptRegistration(參見圖 12)。 ?Figure?12?PageRequestManager 的 ProcessScriptRegistration private void ProcessScriptRegistration(HtmlTextWriter writer)
{
owner.ScriptRegistration.RenderActiveArrayDeclarations(
updatePanelsToRefresh, writer);
owner.ScriptRegistration.RenderActiveScripts(
updatePanelsToRefresh, writer);
owner.ScriptRegistration.RenderActiveSubmitStatements(
updatePanelsToRefresh, writer);
owner.ScriptRegistration.RenderActiveExpandos(
updatePanelsToRefresh, writer);
owner.ScriptRegistration.RenderActiveHiddenFields(
updatePanelsToRefresh, writer);
owner.ScriptRegistration.RenderActiveScriptDisposes(
updatePanelsToRefresh, writer);
}
在 這個位置上,腳本引用將轉化為 ASP.NET AJAX 的頁面輸出。每個方法都將接受傳遞給它的 HtmlTextWriter 以供 Render 階段使用,并寫入必要的內容。每個方法(比如 RenderActiveScripts)都將分別引用以前填入的各個內部數組。
將 AJAX 置入 ASP.NET AJAX 如 果您使用 Fiddler HTTP 調試器代理程序 (fiddlertool.com/fiddler),就可以跟蹤所有流經您計算機上 Internet Explorer? 的 Web 通信。瀏覽 ajax.asp.net 上的 AJAX 示例,通過 Fiddler 查看究竟發生了什么;您可以看到,每次調用一個部分回發事件時都會發生一次真正的 HTTP POST。HTTP POST 包含常規的 HTTP 頭,而且還包含您可能以前從未見過的標頭: x-microsoftajax: Delta=true
此 標頭十分關鍵。ScriptManager 控件一旦識別此標頭,它不會被動地將引用注入到頁面輸出,而是檢查表單 POST 中發送的數據,然后以客戶端腳本能夠理解的格式將響應呈送給客戶端。 除 了將服務和腳本引用寫入到最初的頁面輸出之外,ScriptManager 控件還將初始化客戶端功能。在第一次呈現時,ScriptManager 控件要確保兩個啟動腳本通過 ClientScriptManager 完成注冊。一個腳本調用一個對客戶端運行時進行初始化的方法。另一個腳本對前面討論的 PageRequestManager 類客戶端版本進行初始化。出于對 AJAX 的了解,第二個初始化腳本(即初始化 PageRequestManager 的腳本)最為重要。 該 客戶端腳本通過一個叫做 RenderPageRequestManagerScript 的方法寫入到頁面輸出中。我刪除了該方法中的許多非關鍵代碼,使其更易于閱讀,但一般來說它類似于以下代碼: internal void RenderPageRequestManagerScript(HtmlTextWriter writer)
{
...
writer.Write(_owner.UniqueID);
...
writer.Write(_owner.IPage.Form.ClientID);
...
RenderUpdatePanelIDsFromList(writer, _allUpdatePanels);
...
writer.Write(GetAsyncPostBackControlIDs(true));
...
writer.Write(GetPostBackControlIDs(true));
...
}
請 留意 UpdatePanel、PostBackControl ID 和 AsyncPostBackControl ID 是如何寫到頁面中的;這些控件都是體驗 ASP.NET AJAX 所需的控件。客戶端 PageRequestManager 負責跟蹤所有注冊到 ScriptManager 的控件生成的事件;RenderPageRequestManagerScript 方法用它應該查看的確切控件對客戶端上運行的 PageRequestManager 進行初始化。 當 客戶端引發一個回發事件時,PageRequestManager 將確認該事件是否由 RenderPageRequestManagerScript 中編寫的腳本所指明的任何控件所引起。如果是,PageRequestManager 就會取消該回發事件,然后重新打包。隨后,來自回發事件的新打包的數據將使用客戶端類 Sys.Net.WebRequest(這是公用類并可從您的客戶端代碼中使用)傳輸到服務器。標頭 x-microsoftajax:通過 Sys.Net.WebRequest 發送到服務器的 POST 請求中將會設置 Delta=true。 在 服務器端,ScriptManager 控件現在已被實例化并被載入頁面的控件樹。ScriptManager 是一個服務器端控件,所以會通過 ASP.NET 的 LoadPostData 方法知悉表單 POST 中的數據,該方法允許各控件對表單 POST 進行過濾,以獲得相關的信息。(請注意,這是一個標準事件,并非專用于 ASP.NET AJAX。)若參照圖 10 中的事件圖表,您會看到 LoadPostData 在頁面上的 InitComplete 之后立即發生。在 LoadPostData 內部,ScriptManager 控件識別導致表單 POST 的控件(如果適用,還包括控件所在的 UpdatePanel)。引起回發事件的控件的標識將被收藏起來,供以后使用。 到 目前為止,ScriptManager 控件已認識到正在發生部分回發,并找出了引起回發的控件。現在,您可以構建對客戶端的響應。ScriptManager 控件徹底覆蓋了其宿主頁面的默認 Render 方法,接管了對 ASP.NET 頁面的呈現。要知道,到目前為止,您一直只是將 ScriptManager 控件當作調整您的 ASP.NET AJAX 應用程序中各種設置的某種手段。現在,最終可以看到,您向它提供這些各種不同的選項,因為它(應該)可以成為您的新頁面類,它自身具有完備的格式,可將各 控件呈現為頁面輸出流。 實 際上,ScriptManager 將確保兩個對象的默認呈現方法已被覆蓋:一個是頁面自身,另一個是頁面上的 Web 窗體。因此,當 ASP.NET 頁面框架請求頁面呈現時,將調用 ScriptManager 自身的內部實現。通過了解哪些控件引起回發及其相互關系(是與其他控件同在一個 UpdatePanel 內?還是回發事件源鏈接到了某個 UpdatePanel?),ScriptManager 控件就會知道應該要求哪些子控件進行呈現,哪些子控件應予忽略。在 ASP.NET 中某個 Page 類的默認行為要求每個子控件呈現的情況下,ScriptManager 只會對它認為有必要的子控件提出請求。 同 樣值得注意的是,在覆蓋 Form 對象的呈現方法過程中,ScriptManager 對于所有使用 ScriptManager 注冊的額外數據,都將通過 RegisterDataItem 方法進行處理并發送到客戶端。因此,在 Form 對象得到將自身呈現給客戶端的請求之前,您必須準備好要發送給客戶端的數據,這一點十分重要。發送給客戶端的數據將采用原始格式或 JavaScript Object Notation (JSON) 序列化格式編碼。 最 后,客戶端框架從服務器獲取異步響應并解析出數據。ScriptManager 控件已將所有的控件 ID 和新標記打包到響應中,因此客戶端框架只需在瀏覽器的文檔對象模型上完成腳本處理即可更新頁面內容。如果整個處理過程都以異步方式進行,瀏覽器將得到快速 的自動更新,Web 頁的用戶將獲得更好的體驗。
研習腳本 ASP.NET AJAX 是一項功能強大的技術。為了在您的 Web 應用程序中充分利用 ASP.NET AJAX 的許多功能,您需要按照本文所演示的方式使用 ScriptManager 控件。 ScriptManager 控件為您處理 ASP.NET AJAX 實現過程中的許多細節。現在您應該注意到了,在 UpdatePanel 等控件的默認行為并非您真正需要的情況下,就會感覺到 ScriptManager 的頻繁出現。除了腳本功能和 AJAX 之外,ScriptManager 控件還支持身份驗證和個性化等高端功能。 腳 本和服務必須使用 ScriptManager 控件通過頁面的 PreRenderComplete 事件進行注冊。在完全回發或部分回發過程中,可將腳本引用添加到頁面中。當注冊腳本時,要求其他對象將其所有腳本和服務引用真正遞交給 ScriptManager。 最 后,通過改寫宿主頁面默認的呈現實現方法,ASP.NET AJAX 在客戶端得以實現。從此,ScriptManager 將掌管頁面的呈現循環,并向客戶端發送一個易于解析的響應,客戶端用它來更新瀏覽器中的元素。通過改寫默認的頁面呈現方法,可將合適的控件以特定的格式呈 現給客戶端,包括使用 ScriptManager 注冊的數據。
Ben Rush是 一名中西部地區的 Microsoft .NET 顧問,他專門研究 ASP.NET 技術。在 ben-rush.net/blog 上,您可以了解更多有關 Ben 的信息。
以上是mscn的原文轉載,以前只是簡單使用ScriptManager,后來發現他并不是我使用的那么簡單,這篇文章對內幕有個大概了解,但還不太理解
?
轉載于:https://www.cnblogs.com/freeton/archive/2010/04/25/1719754.html
總結
以上是生活随笔為你收集整理的ScriptManager 帮助您实现 Web 应用程序的 AJAX 功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 程序员的语言“艳遇史”(一)——班长pa
- 下一篇: 数据库 ACCESS与SQL SERVE