一、 簡介
首先,我們注意到,ASP.NET 2.0中也提供了一個伺服器控制項ImageMap。此控制項是一個讓你可以在圖片上定義熱點(HotSpot)地區的伺服器控制項。使用者可以通過點擊這些作用區進行回傳(PostBack)操作或者轉寄到某個URL地址。典型情況下,該控制項用於需要對某張圖片的局部範圍進行互動操作。然而,這個控制項的不足之處在於,在點擊這些作用區進行回傳時將導致整個Web頁面的重新整理。
在本文中,我們將基於ASP.NET AJAX技術對普通的HTML Map控制項加以擴充,以達到在點擊其上的作用區時,在顯示有關詳細資料時僅僅導致局部的頁面更新,從而使之適應Web 2.0應用程式開發潮流。
下面圖1展示了本文樣本程式運行時的一個快照。
圖1.使用AJAX技術擴充後的Map控制項熱點點擊僅引發局部更新。
從上圖中看到,當滑鼠懸浮於上圖太陽系中的木星(木星)上時,有關該星球的細節資訊將以一個快顯視窗形式友好地展示出來(註:此圖取自MSDN,這裡沒有翻譯相應單詞)。
二、 建立一個AJAX樣本網站
啟動Visual Studio 2005,選擇“檔案→建立網站…”,然後選擇“ASP.NET AJAX-Enabled Web Site”模板,命名工程為“Ajax_ImageMap”,並選擇C#作為內建支援語言,最後點擊OK。
然後,添加一個新的ASPX頁面ImageMap.aspx,並且按如下所示修改其中的HTML代碼部分:
以下是引用片段:
<IMG SRC="imagessolarsys.gif" WIDTH=504 HEIGHT=126 BORDER=0
ALT="Solar System" USEMAP="#SystemMap">
<MAP NAME="SystemMap">
<AREA SHAPE="rect" COORDS="0,0,82,126"
onmouseover="javascript:GetAreaInfo(event, 'sun');" onmouseout="javascript:HidePopup();">
<AREA SHAPE="circle" COORDS="90,58,3"
onmouseover="javascript:GetAreaInfo(event, 'merglobe');" onmouseout="javascript:HidePopup();"
>
<AREA SHAPE…………(省略)
</MAP>
在上面代碼中,我們添加了一個HTML 元素和一個HTML 元素(註:VS2005工具列中沒有提供現成的控制項,只能手工添加)。其中定義了各個星球相應的熱點形狀及座標資訊。而且,每一個熱點都有一個相應的onmouseover和onmouseout JavaScript函數與之相關聯。當滑鼠在這些熱點上移動時,這兩個函數將被啟用,相應資訊被顯示出來。有關這兩個函數,我們將在後面詳細討論。
三、 建立一個AJAX服務
現在,我們需要建立一個新的Web服務,由它負責與熱點點擊相關的資料檢索任務。其實,這裡所謂的“AJAX服務”,其功能與通常的Web服務是一致的。有關它們之間的細節區別在此不再贅述。現在,你可以右擊工程,然後添加一個命名為LocationService.asmx的Web服務。
注意,在本例中我們僅想通過這個Web服務來類比實戰環境中的一種簡單邏輯。因此,它僅包含一個Web方法;此方法負責類比從伺服器資料庫中取得用戶端需要的資訊。
在此,為了使這個ASP.NET Web服務能夠被從用戶端以AJAX方式加以調用,必須把ScriptService屬性添加到類聲明的前面,如下所示:
以下是引用片段:
[ScriptService()]
publicclassLocationService:System.Web.Services.WebService
{
現在,編寫我們的Web方法:
以下是引用片段:
[WebMethod]
[ScriptMethod(UseHttpGet=false,ResponseFormat=ResponseFormat.Json)]
publicstringGetAreaInfo(stringarea)
{
returnarea;
}
根據權威人士建議,為了安全起見,我們一般要使用HttpPost(或者HttpGet= false)方式訪問Web方法。然後,我們把返回的資料格式配置為JSON格式(預設即為JSON方式)。
為了簡化起見,這裡的GetAreaInfo方法僅僅返回輸入參數的相同值;但在實際開發中,我們應該在此替換以從資料庫中檢索資料。
到目前為止,我們已經成功建立從用戶端以AJAX方式加以調用的Web服務。
但是,我們還要對頁面中的伺服器控制項ScriptManager進行一些適當的配置,如下所示:
以下是引用片段:
<asp:ScriptManager ID="ScriptManager1" runat="server">
<services>
<asp:servicereference path="~/LocationService.asmx" />
</services>
</asp:ScriptManager>
在此,我們僅在節點下加入了一個服務參考,但其作用如何呢?
從產生的HTML源碼分析,上面的配置將產生如下所示內容:
以下是引用片段:
<script src="LocationService.asmx/jsdebug" type="text/javascript"></script>
這裡的指令碼標籤引用了一個JavaScript檔案LocationService.asmx/jsdebug。其實這是一個Web服務代理類。正是通過此代理類,我們才得以從用戶端以非同步方式調用伺服器端的Web服務。
更有意思的是,如果你簡單地複製顯示在上面的路徑到瀏覽器中,你將看到一個在運行時刻由AJAX環境產生的JavaScript檔案—此檔案使指令碼服務調用可用。有關代理類,我們不再深入討論。
下面,我們來看如何建立一個定製的用戶端類。
四、 建立用戶端定製類
我們知道ASP.NET AJAX架構的重大“發明”之一就是,它引入了物件導向的JavaScript編程模型。現在,藉助於JavaScript設計模式,我們可以輕鬆地建立自己的模板或類,加入繼承概念,建立介面與枚舉等。
在本文中,我們將開發一個封裝所有本樣本中要求功能的用戶端類。
現在,右擊工程,並新添加一個名為ImageMap的JavaScript檔案。在此檔案中,我們將定義一個新的命名空間MyServices;這個命名空間將包含我們要開發的用戶端類。如下所示:
以下是引用片段:
Type.registerNamespace("MyServices");
接下來,我們定義要建立的用戶端類的建構函式:
以下是引用片段:
MyServices.Location = function (uiElement, uiBody) {
MyServices.Location.initializeBase(this);
this._uiElement = uiElement;
this._uiBody = uiBody;
this._xAxis = 0;
this._yAxis = 0;
}
一個模板或類的建構函式也只不過是一個普通的JavaScript函數。該構造器共有兩個參數:uiElement和uiBody。
這兩個參數都將用於描述在頁面顯示的快顯視窗。另外兩個私人變數_xAxis和_yAxis用於描述快顯視窗的顯示位置。典型情況下,我們最好在構造器中聲明所有的私人成員。
接下來,我們將使用原型設計模式編寫該類中的成員函數和屬性:
以下是引用片段:
MyServices.Location.prototype =
{
get_uiElement: function()
{
return this._uiElement;
},
set_uiElement: function(value)
{
this._uiElement = value;
},
get_uiBody: function()
{
return this._uiBody;
},
set_uiBody: function(value)
{
this._uiBody = value;
},
注意,這裡的UI元素屬性方法的定義方式非常類似於.NET中各種語言中的定義形式。
下面的成員函數是我們的重點,它負責調用遠端Web服務:
以下是引用片段:
ShowPopupinfo: function(event, areaName)
{
MyServices.LocationService.GetAreaInfo(areaName,
Function.createDelegate(this, this.OnCompleted),
this.OnError, //負責進行錯誤處理的回叫函數
this.OnTimeOut); //負責進行逾時處理的回叫函數
this._xAxis = event.clientX;
this._yAxis = event.clientY;
}
上面的代碼展示的是非常典型的從用戶端調用Web服務的方法:
1)形式與調用一個普通的本地方法幾乎一樣方便;
2)Function.createDelegate函數是ASP.NET AJAX用戶端開發中的極為重要的全域函數。建立此函數的原始目的之一是解決this關鍵字的問題。在一個由一個DOM元素引發的事件處理器中,this關鍵字總是引用此DOM元素而不是類本身。但在此,我們使用這個函數的理由是,使得AJAX環境在與激發Web服務的相同的類執行個體中調用成功時的回叫函數。當你需要引用用戶端類的屬性和方法時,這是相當重要的。簡言之,使用此函數將使得訪問調用Web服務的用戶端類的屬性和方法安全而準確。否則,進行非同步呼叫的用戶端類執行個體將為null,因為Web服務的響應是在另一個不同的上下文中執行的—這個上下文不再等同於發出非同步Web調用請求的那個上下文。
3)有意思的是,這裡的GetAreaInfo方法並不是我們在前面建立的Web服務中的那個,而是屬於在運行時刻建立的Web服務代理類—此代理類作為一個用戶端代理訪問伺服器端的ASMX Web服務。
上面ShowPopupInfo函數中的最後兩行代碼中,使用事件的輸入參數設定兩個私人變數xAxis和yAxis的值。我們在此的目的是,在與使用者點擊位置儘可能近的地方顯示快顯視窗。
下面是調用成功時對應的回叫函數的實現代碼:
以下是引用片段:
OnCompleted: function(result, userContext, methodName)
{
var uiElement = $get(this.get_uiElement());
var uiBody = $get(this.get_uiBody());
if (uiBody != null)
{
var textNode = uiBody.firstChild;
if (!textNode)
{
textNode = document.createTextNode(result);
uiBody.appendChild(textNode);
}
else
{
textNode.nodeValue = result;
}
if (uiElement != null)
{
uiElement.style.visibility = "visible";
uiElement.style.display = "inline";
uiElement.style.left = this._xAxis + "px";
uiElement.style.top = this._yAxis + "px";
}
}
},
內容相當簡單—把從伺服器端返回的資料設定為快顯視窗的顯示內容並根據情況確保顯示此視窗。
在建立用戶端類的最後,我們還必須告訴AJAX架構在用戶端註冊之,以便可以從用戶端訪問它:
以下是引用片段:
MyServices.Location.registerClass("MyServices.Location");
至此,用戶端類MyServices.Location已經成功建立。那麼,如何使用它呢?
首先,我們需要在頁面載入時定義用戶端類的一個新的執行個體。為此,我們需要在pageLoad函數中編程:
以下是引用片段:
varlocation=null;
functionpageLoad(sender,args){
location=newMyServices.Location("modal","modalBody");
location.HidePopupInfo();
}
上面的代碼簡單地建立MyServices.Location類的一個新的執行個體。然後調用用戶端類的成員函數之一來隱藏頁面中的快顯視窗。為什麼我們在pageLoad函數中建立用戶端類的一個執行個體呢?原因在於,當AJAX環境控制流程程到達pageLoad函數時,所有的AJAX用戶端和使用者定義的JavaScript代碼都已經被成功載入。因此,這一時刻我們可以安全地訪問任何使用者或系統定義的JavaScript代碼。
其它幾個工具函數比較簡單,在此不再贅述。
五、 總結
在本文中,我向你展示了如何通過建立一個AJAX服務和建立自己定製的用戶端類來擴充HTML Map控制項。在擴充後的控制項中,當點擊映像中的某個地區時,我們可以通過新型的AJAX方式來給出相關細節資訊,而不必重新整理整個Web頁面。儘管在大部分Web應用中我們較少應用到這種Map控制項(也許因此VS2005工具列中省略之),但如果開發大量圖片、映像及地圖操作相關的Web應用時,基於本文AJAX改造後的Map控制項一定會讓你的Web應用更加絢爛多彩。