一、簡介
到目前為止,我們已經讨論了開發Ajax控件所涉及的用戶端相關技術。現在,讓我們來讨論此過程中與伺服器端相關的一些技術。
需要說明的是,在【用戶端】篇中我們的舉例本質上僅是使用ASP.NET AJAX架構提供的面向對象JavaScript技術來增強了一個用戶端圖像元件,而沒有明顯涉及到AJAX技術(除了ScriptManager在背景 以AJAX方式下載下傳并管理用戶端腳本代碼外)。是以,這個例子是簡單的,僅憑用戶端相關知識就可以使用這個增強控件。
但是,在實際開發中,當要增強的用戶端控件涉及到AJAX技術時,或者幹脆是想增強伺服器端元件(如UpdatePanel控件)時,我們必須進行 相關的伺服器端程式設計,而這要求我們必須對Ajax控件開發中所涉及的伺服器端相關聯的類有所了解。而且,還要以ASP.NET 2.0伺服器控件開發相關知識為基本前提,特别是在開發複雜的Ajax控件時。
在本篇中,我們要重新構造一個增強的圖像按鈕控件MySrvImageButton,此控件将以ASP.NET 2.0伺服器控件ImageButton為基礎。
二、AJAX控件開發伺服器端相關技術
首先,讓我們來看一下AJAX控件開發伺服器端相關元件及其關系,這些類之間的繼承關系圖如下圖1所示。

圖1:控件開發涉及的主要伺服器端類之間層次結構圖
上圖展示了元件、控件和擴充器之間的繼承關系。如你所見,為了開發一個控件(注意,Component和Extender不在本文讨論範圍之内), 我們有兩個選擇:其一,建立一個派生自ScriptControl的類;其二,建立一個實作IScriptControl接口的類。但是,如果你想使你的 控件從WebControl派生,那麼,ScriptControl應該是一個更好的選擇—因為它正是派生自WebControl控件本身。但是,如果你 想從頭開發建立你的控件,并且不要求實作WebControl所具備的任何内在特征,那麼,實作IScriptControl則更為恰當。此外,當你想在 一個現有控件(例如本文中的MySrvImageButton)中添加Ajax特征時選擇使用接口IScriptControl也會是你的選擇。但是這兩 種方法都要求重載下列兩個方法:①、GetScriptDescriptors;②、GetScriptReferences。
三、GetScriptDescriptors
這個方法負責在用戶端以自動方式生成$create語句(而在上篇中是使用手工方式)。它使用了一個特殊類ScriptDescriptor來生成它。在繼續往下讨論前,首先讓我們來浏覽一下ScriptDescriptor類中的繼承層次圖(如圖2所示):
圖2:ScriptDescriptor類中的繼承層次圖
顯然,每一種具體類型的元件都存在其單獨的描述符。如果你在開發一個正常元件,那麼,這個方法會傳回一個 ScriptComponentDescriptor的執行個體。對于擴充器而言,該方法傳回的是一個ScriptBehaviorDescriptor實 例;而對于一個控件,則傳回ScriptControlDescriptor類的執行個體。該描述符中提供了一些特定的方法用于建立伺服器端與用戶端的“連 接”。下面,還是讓我們來分析一個有關$Create語句是如何在伺服器端“注入”的簡短示例:
//上篇中從用戶端以手工方式使用$create
$create(AjaxImageButtonNamespace.MyCliImageButton,
{'hoverImageUrl':'Images/updateh.gif'},
{'click':buttonClicked}, null, $get('cliBtn'));
在本文中,我們在伺服器端以下列幾個語句共同自動生成$Create語句:
ScriptControlDescriptor desc =
new ScriptControlDescriptor("AjaxImageButtonLib.MySrvImageButton",
ClientID);
if (!string.IsNullOrEmpty(HoverImageUrl))
{
desc.AddProperty("hoverImageUrl", HoverImageUrl);
}
if (!string.IsNullOrEmpty(ClientClickFunction))
desc.AddEvent("click", ClientClickFunction);
return desc;
在上面的代碼中,我們在構造器中傳遞用戶端類型(盡管用戶端和伺服器端具有相同的類型名)和DOM元素ID。這一步将填充$Create語句的第一 個和最後一個參數。此後,我們設定hoverImageUrl屬性,它對應于$Create語句的屬性部分。最後,通過設定Click事件處理器,我們填 充$Create語句的事件部分。下面是腳本描述符中暴露的幾個重要的方法:
1)AddProperty()
這個方法能夠在用戶端添加一個屬性。第一個參數相應于屬性名,第二個參數對應該參數的取值。
2)AddEvent()
這個方法在用戶端添加一個事件。第一個參數相應于事件名,第二個參數對應你想綁定的函數的名稱。
3)AddScriptProperty()
這個方法能夠把JavaScript代碼指定為屬性的一個值。對于複雜的屬性指派通常都要求這樣的操作。
4)AddElementProperty()
這個方法在用戶端添加一個屬性,但是其與AddProperty方法間的差別在于,該值作為一個參數傳遞給$get方法。
5)AddComponentProperty()
這個方法負責在$Create語句的Component部分添加一個元件引用。該值将被用于$find語句中實作屬性的指派。這個方法的第一個參數相應于屬性名,第二個參數對應該元件的id。
四、GetScriptReferences
這個方法負責在ScriptManager中注冊JavaScript檔案。在這個方法中,針對每一個要求的JavaScript檔案,我們都需要建立一個相應的ScriptReference執行個體。例如,在圖像按鈕例子中,我們按如下方式注冊JavaScript檔案:
此外,我們還可以以嵌入式資源方式注冊JavaScript檔案。為此,我們必須首先在Visual Studio的解決方案資料總管中把一個JavaScript檔案标記為一種嵌入式資源。然後,我們必須添加上WebResource屬性以便利用 Asp.Net 2.0 Web資源處理器。下列代碼展示了如何把一個JavaScript檔案注冊為一個嵌入式資源:
[assembly: WebResource("MyControls.SubControl.Script1.js", "text/javascript")]
namespace MyControls{
public class SubControl: Control, IScriptControl
//………
IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
return new
ScriptReference(this.Page.ClientScript.GetWebResourceUrl(
this.GetType(), "MyControls.SubControl.Script1.js"));
此外,如果我們想實作IScriptControl接口而不是從ScriptControl中繼承的話,我們還必須重載OnPreRender與Render方法。這樣将能確定ScriptManger識别出該伺服器控件是一個支援Ajax功能的控件。
下列代碼展示了開發基于ASP.NET 2.0伺服器端控件的AJAX控件時必須在伺服器端實作的最少代碼:
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DummyNamespace
public class DummyControl : Control, IScriptControl
public DummyControl(): base()
{}
protected override void OnPreRender(EventArgs e)
base.OnPreRender(e);
ScriptManager scriptManager = ScriptManager.GetCurrent(Page);
if (scriptManager == null)
throw new InvalidOperationException(
"此頁面中必須存在一個ScriptManager控件!");
scriptManager.RegisterScriptControl(this);
protected override void Render(HtmlTextWriter writer)
base.Render(writer);
writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);
writer.RenderBeginTag(HtmlTextWriterTag.Div);
writer.Write("這是一個啞元控件");
writer.RenderEndTag();
if (!DesignMode) {
ScriptManager.GetCurrent(this.Page).RegisterScriptDescriptors(this);
IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
//因為這僅是一個啞元控件,是以我們隻是建立一個新的執行個體
return new ScriptControlDescriptor("DummyNamespace.DummyControl", ClientID);
return new ScriptReference(Page.ResolveUrl("~/DummyControl.js"));
上面代碼中,我們在OnPreRender方法中實作注冊控件,而在Render方法中注冊腳本描述符部分。當然,如果頁面上不存在 ScriptManager控件的話,我們必須抛出一個錯誤提示。但是,如果你正在開發非基于ASP.NETAJAX架構的控件的話,你完全可以從頁面中 删除ScriptManager控件。
五、建立基于ASP.NET伺服器端控件的增強AJAX圖像控件
(一)建立示例AJAX網站
啟動Visual Studio 2005,選擇“檔案→建立網站…”,然後選擇“ASP.NET AJAX-Enabled Web Site”模闆,命名工程為“AjaxServCtrlTest”,并選擇C#作為内置支援語言,最後點選“确定”。
(二)建立AJAX技術支援的增強伺服器控件
點選菜單“檔案→添加→建立項目…”,在“添加新項目”對話框中,從左邊選擇“項目類型”為“Visual C#→Windows”,從右邊選擇“模闆類型”為“Web控件庫”,輸入控件庫的名字為AjaxImageButtonLib,選擇目标目錄為前面建立 的網站根目錄,最後點選“确定”。
接下來,根據我們前面的分析,把類庫源WebCustomControl1.cs檔案的内容更改為以下形式:
//…………(省略命名空間引用部分)
namespace AjaxImageButtonLib
public class MySrvImageButton :
System.Web.UI.WebControls.ImageButton, IScriptControl
public string HoverImageUrl
get
object value = ViewState["hoverImageUrl"];
return (value == null) ? string.Empty : (string)value;
set
ViewState["hoverImageUrl"] = value;
public string ClientClickFunction
object value = ViewState["clientClickFunction"];
ViewState["clientClickFunction"] = value;
public MySrvImageButton()
: base()
throw new InvalidOperationException
("ScriptManager required on the page.");
if (!DesignMode)
new ScriptControlDescriptor("AjaxImageButtonLib.MySrvImageButton", ClientID);
yield return desc;
yield return new ScriptReference(Page.ResolveUrl("~/SrvImageButton.js"));
首先,我們注意到我們要建立的新控件MySrvImageButton繼承自ASP.NET伺服器控件ImageButton,并繼承了接口IScriptControl。
有了前面的理論描述,在此,我們省略對于其中幾個正常方法的描述。
接下來,我們要建構這個控件庫(即程式集)。右擊此庫工程,并點選“生成”指令以生成程式集AjaxImageButtonLib.dll,此庫檔案中即包含了我們的伺服器控件。
(三)建立用戶端JavaScript代碼
這裡建立的用戶端控件類相應的JavaScript檔案ImageButton.js在内容上完全相同,隻不過為了差別起見,我們進行了某些地方的重新命名罷了。在此不再贅述。
(四)在示例網頁中應用建構的新控件
以滑鼠右擊前面網站AjaxServCtrlTest,把它設定為“啟動項目”。事實上,因為這個網站工程與前面的類庫工程建立于同一個方案下,所 以,在前面生成程式集AjaxImageButtonLib.dll的一結束,建立的伺服器控件就被自動添加到Visual Studio 2005工具欄中,如下圖3所示。于是,我們可以直接把這個控件拖動到示例網頁Default.aspx中。
圖3:拖動建立的伺服器控件到示例網頁中
根據前面的控件代碼實作,不出所料,點選上圖3中的圖形按鈕控件,即可在其相應的“屬性”對話框中設定這個控件的hoverImageUrl屬性,而且指定其ClientClickFunction方法(其實正是此控件的click事件處理器函數指針)。
注意,因為控件代碼中的方法GetScriptReferences已經為我們自動生成了前面提到的$create方法,是以我們不需要再在ScriptManager中注冊在本篇中建立的JavaScript檔案—SrvImageButton.js了。
(五)運作及性能簡析
現在,請按F5鍵運作此頁面并移動滑鼠到圖像按鈕上觀察,你會注意到結果與上篇中的效果一緻(即在滑鼠移動切換新圖像時,這些動作都發生于客戶浏覽器端而不再與伺服器端相關)。下圖4相應于此示例頁面運作時刻快照。
圖4:示例網頁運作時刻螢幕快照
通過以滑鼠右擊網頁并選擇彈出菜單中的“檢視源檔案”觀察上、下篇中示例頁面相應的源碼,我們會注意到其内容基本是一緻的。另外,通過使用Fiddler觀察這兩個示例頁面下載下傳到用戶端時各子產品的大小,你也會注意到基本一緻,如下面圖5所示。
圖5:兩個示例頁面下載下傳到用戶端時各子產品大小比較
因為本文兩個例子極為簡單,是以其性能基本平衡。但随着服務端程式設計的複雜化,本篇中基于伺服器端控件的擴充方案應該有較大的性能損耗。但應該仍具有 令人滿意的效果,這也正是AJAX Control Toolkit控件數量急劇增加的重要原因之一。而上篇中的方案基于“純粹”(相對而言)的用戶端,即使性能上與本篇中方案相差無幾,但是卻明顯多出了跨 越伺服器端平台的優勢,這也正是上篇中方案吸引人的主要原因。
六、總結
雖然以上、下兩個篇幅形成此文,但是這也僅能通過簡短的例子向你闡述了開發ASP.NETAJAX架構中的Ajax控件所涉及的主要技術。盡管目前 的ASP.NET AJAX架構已經形成正規的1.0版本,而且這個架構為基于AJAX技術開發以ASP.NET 2.0為主的Web應用提供了全方位支援,但是這個架構仍然在許多方面有待改進。事實上,我們可以進一步沿着ASP.NET AJAX用戶端與伺服器端架構層次關系圖進一步擴充其底層。當然,在此架構與Visual Studio整合方面也存在相當的挖掘潛力。
如今,随着微軟Silverlight技術的推出,ASP.NET AJAX架構的重要性日顯突出。自然,與此架構相關的控件開發也必将在這一大環境中占居着重要的位置。
本文轉自朱先忠老師51CTO部落格,原文連結: http://blog.51cto.com/zhuxianzhong/59816,如需轉載請自行聯系原作者