ArcGIS Server .NET ADF中的AJAX之深入淺出/CallbackResult詳解
原文:http://bbs.esrichina-bj.cn/ESRI/thread-61303-1-5.html
論壇中已有不少關於.NET
ADF中AJAX的精彩講解,但切入點有些抽象。本文試圖從一個初學者的角度著眼,從AJAX講起,由淺入深,去掉不必要的技術細節,使大家對.NET
ADF中AJAX有清晰的理解。再輔以前人的教程,由此能更充分地掌握.NET ADF中AJAX的工作原理。
經常在論壇上看到很多朋友問:為什麼我已經建立了一個callbackresult,但是瀏覽器中卻沒有任何反應?或者我已經把ADF控制項放到了
UpdatePanel裡,但為什麼還是達不到我想要的效果?9.3版本的.NET Web
ADF發布近2年的時間裡,ESRI從來沒有說過:“很抱歉,我們ADF中的AJAX工作流程有時候可能會出現一點問題”,這證明.NET Web
ADF中的AJAX還是足夠可靠的,事實是問題只可能出現在我們的代碼裡。甚至在你根本還沒有弄清如何來正確使用ADF中的AJAX時,就模仿別人的代
碼,寄希望於你也不知道為什麼要建立的一個callbackresult就能夠好好的為你工作,這不應該是經常和只知道循規蹈矩的機器打交道的人所做的事
情。
既然你已經選擇了.NET
ADF,並想利用好其中的AJAX能力,那麼弄清楚它的原理應該是必要的,可以從本文第二部分開始閱讀;但也許你是一個ArcGIS
Server的新手,甚至是ASP.NET的新手,不得不在短時間內完成你手上的GIS任務,並不關心它的原理,那你可以閱讀本文的前兩部分或者只是第二
部分。(如果你是一個網頁開發的新手,那麼請自行學習HTML+CSS+Javascript)
第一部分:1分鐘AJAX時間
典型的,ASP.NET
2.0之前的網頁,當用戶端發送請求的時候,比如點擊“開始處理”按鈕,會將整個頁面資訊提交到伺服器上這個頁面對應的類進行處理(發送的請求叫做
postback),這個頁面類處理完成後將結果(包括頁面原有的資訊)輸出成瀏覽器能夠識別的HTML+CSS+Javascript返回用戶端,你才
會看到結果頁面。提交請求後到結果頁面呈現前,你的滑鼠會呈沙漏狀,無法操作頁面;結果頁面呈現的一瞬間,你會看到整個頁面完全重新整理一次。AJAX的出現
就是為瞭解決這個糟糕的過程,有了AJAX本領的頁面,當你點擊“開始處理”按鈕後,你依然能夠對頁面其他部分進行操作,結果呈現的時候也不會重新整理全部頁
面,而只是悄悄修改需要改變的部分。目前互連網上大多數頁面都有AJAX能力,但不要迷戀AJAX,它只是個效果的名稱而已,你需要感謝的是
ASP.NET中實現AJAX的兩個功臣:ASP.NET 2.0時候出現的Client Callback和ASP.NET
2.0的一個擴充ASP.NET
AJAX。但也不要迷戀ASP.NET,因為在最下面真正幹苦力活的是Javascript裡的XMLHttpRequest對象,前兩者是對其不同程度
的封裝。
最後重複一下,Client Callback和ASP.NET AJAX是並列關係,根據你的需要取其一作為你實現AJAX的手段即可。
第二部分:如何正確使用CallbackResult
任何想正確使用CallbackResult的人應該都不會拒絕我先解釋一下究竟什麼是CallbackResult,那就先來看看它是什麼。
在只有一個Map Control和Toolbar
Control頁面中,無論你如何操作地圖,或者使用Toolbar上的按鈕和工具與地圖互動,都不會重新整理整個頁面,這證明所有Web
ADF的控制項具有AJAX能力,因為其他控制項也是如此。Web ADF中的Server Control是如何?這一點呢?無疑是利用了Client
Callback(9.2版本和以上可用)或者ASP.NET AJAX(9.3版本和以上可用)。究竟怎麼利用?這個問題先別急,讓我們深入一點點。
針對Web
ADF控制項,先來看一個普通的操作:滑鼠拉框放大地圖。它的整個完成過程是這樣的:使用者在用戶端的地圖上(其實是ESRI自己封裝的名為Map的
Javascript對象,屬於Web ADF
Javascript中的內容)用滑鼠拉一個框,鬆開滑鼠的時候地圖對象會將一些資訊,比如這個框框的範圍等,發送到伺服器端,伺服器端對應的Map控制項
會根據伺服器端當前Map的範圍和框框的範圍做計算,保留新的範圍在伺服器的Map控制項上。伺服器端的過程容易完成,但這些伺服器端控制項還需要負責任地將
這些結果同步到用戶端去,否則用戶端看不到任何變化。在這個例子中伺服器端將結果同步到用戶端的過程是,由地圖服務輸出新的地圖映像(如果是緩衝地圖服
務,則給出新範圍的切片)傳送給用戶端。如所示:
下載 (20.31 KB)2010-1-31 00:59
如果不細心可以直接看下一段。如果細心,你可能會問為什麼一個放大操作,會出現那麼多回合的請求和響應?事實是,放大地圖這個操作裡會有兩次請求/
響應過程。請求1:傳輸框框範圍到伺服器;響應1:返回放大後的新地圖範圍給用戶端;(用戶端的地圖控制項收到響應1後立即再次發出)請求2:根據新範圍問
伺服器端地圖控制項所要結果地圖圖片;響應2:傳輸地圖圖片給用戶端。一個放大過程才算完成。結論:對Web
ADF控制項做的一個操作,很可能會引起不只一次請求和響應,但我們只關心且只需要關心最初的請求和最終的結果,因為其他過程Web
ADF控制項會自動替我們完成。
任何對Web ADF控制項的操作都是和上面的過程一樣,用戶端的Web ADF
Javascript對象發送請求,伺服器端相應的控制項處理結果並通過“響應”(伴隨著一系列對我們透明的後續請求和響應)來將結果同步到用戶端去,這個
“響應”就是我們的主角:ADF中的CallbackResult對象(實際上是JSON字串)。它是由伺服器端的Web
ADF控制項產生,用戶端的特定Web ADF
Javascript函數負責解析的。不管產生的CallbackResult長什麼樣,用戶端的負責解析的函數名字都叫
做:processCallbackResult()。如所示:
下載 (32.33 KB)2010-1-31 00:59
為了邏輯上的需要,比如一個動作對應一個CallbackResult,於是就有了的CallbackResultCollection。在伺服器
端,每個Web ADF
Control都有一個Callbackresults屬性(CallbackResultCollection),為什麼需要這樣呢?必須記住一點:
“誰請求,誰管理”,即(用戶端)發起請求的那個控制項(在伺服器端所對應的控制項)負責傳回CallbackResults。
CallbackResults傳回到用戶端後,Web ADF
Javascript中的processCallbackResult()函數就會自動對其進行解析。所以開發人員所需要做的工作就是,將伺服器端所有產
生的CallbackResults交付給負責帶回CallbackResults的那個控制項——加入控制項的CallbackResults屬性中。你並
不需要關心CallbackResults到了用戶端是如何解析的,因為這一切都由processCallbackResult()自動完成。因為請求可
能由任何一個控制項觸發,所以每個Web ADF控制項都有一個CallbackResults屬性。
比如,運行時動態給Map控制項添加了新的地圖服務。在這個過程中,Map控制項會發送請求,那麼自然是伺服器端的Map控制項負責將
CallbackResults帶回用戶端。而頁面中的TOC控制項為了反映出這個變化,在伺服器端就要對其進行重新整理,重新整理這個動作就會使TOC控制項自身產
生CallbackResults。記住“誰請求,誰管理”,Map請求,Map管理。如果不將TOC重新整理的結果通過Map的
CallbackResults帶回用戶端,那麼頁面上的TOC是不會有任何變化的。正確的做法是:
- ... <new map resource item added>
- Toc1.Refresh();
- Map1.CallbackResults.CopyFrom(Toc1.CallbackResults);
複製代碼
整個過程如所示:
下載 (21.29 KB)2010-1-31 00:59
不是所有的Web ADF變化都需要我們手動處理,下面列出了一些Web ADF控制項之間的內在關係:
- 已經綁定到Map的Toolbar控制項上,任何command或tool執行過程中,會自動將Map的CallbackResults拷
貝到Toolbar的CallbackResults中。所以如果在自訂command或tool裡對Map做了修改,則不需要將其產生的
CallbackResults拷貝給負責帶迴響應的Toolbar控制項;
- 已經綁定到Map的TOC控制項,NodeChecked事件
會自動將Map產生CallbackResults拷貝給TOC控制項;當Map和TOC中包含有可見度依賴比例尺變化而變化的圖層
時,ScaleChanged事件會將Toc的CallbackResults拷貝給Map控制項;
- ScaleBar綁定到Map後,ScaleBar的PreRender事件會將ScaleBar的CallbackResults拷貝給Map控制項;
- MapCopyrightText控制項綁定到Map後,添加或移除map resource item會將MapCopyrightText的CallbackResults拷貝給Map控制項;
- Magnifier綁定到Map後,改變Map的顯示範圍或初始化tiling scheme時,會將Magnifier的CallbackResults拷貝給Map控制項;
- FloatingPanel的Refresh()方法會將其中所有子控制項CallbackResults拷貝到自身的CallbackResults中;
- TaskResults
綁定到Map後,執行一個task會將Map的CallbackResults拷貝給TaskResults控制項,TaskResults的
CallbackResults隨後會自動拷貝給這個執行的task。所以如果你手動將Map的CallbackResults拷貝給這個task的
CallbackResults的話,將會產生重複的結果;
- TaskResults控制項有預定義的ContextMenus。這些右鍵功能表項目的ItemClicked事件會將TaskResults和Map的CallbackResults自動拷貝給ContextMenu。
這些內在關係在下面的Web ADF控制項一覽圖中可見一斑。除了以上事件,其他情況下的CallbackResults都需要你自己將其添加到負責帶迴響應的控制項中。
下載 (169.51 KB)2010-1-31 00:59
另外,還可以在伺服器端自訂CallbackResult,來達到修改用戶端非Web ADF控制項或者執行一段Javascript代碼的目的。自訂CallbackResult可已通過其建構函式或靜態方法來實現,這裡不再贅述:
下載 (46.95 KB)2010-1-31 00:59
當然,別忘了將自己建立的CallbackResults拷貝到正確的控制項中。
至此,如何正確使用CallbackResult的問題已經講完了,這一切叫做Web ADF中callback
result架構。它給我們提供了統一的編程體驗,而你則可以根據自己的需要和編程經驗來選擇使用Client Callback還是ASP.NET
AJAX。最後鄭重提醒一點:如果使用了ASP.NET AJAX方式,在頁面中強烈建議讓所有Web
ADF控制項遠離UpdatePanel。因為它們本身就能夠註冊到ScriptManager中,在完全重新整理自己的情況下即可完成必要的內容更新。如果你
將Web ADF控制項放在UpdatePanel中,雖然能夠正常工作,但重新整理整個Web ADF控制項。
第三部分:深入理解Web ADF中的AJAX
現在來看看前面提出的問題:Web ADF究竟是如何利用ASP.NET中的Client Callback或是ASP.NET
AJAX的?首先明確一點,Web ADF控制項只能使用一種非同步通訊方式,要麼是Client Callback,要麼是ASP.NET
AJAX。如果頁面中有ScriptManager控制項,就意味著你選擇了ASP.NET AJAX方式進行非同步通訊,那麼Web
ADF控制項的實際運行中也會採用這種方式;反之如果頁面中沒有ScriptManager控制項,那麼Web ADF實質上會採用Client
Callback方式來進行非同步通訊。下面分別通過幾個情境來看看兩種方式的實際操作區別:
如果採用了Client Callback方式:
- 情境一:Web ADF控制項來產生請求和響應,用戶端processCallbackResult()來處理響應
這個過程就是第二部分中假設的幾個情境,請求由一個Web
ADF控制項產生,伺服器端進行處理,也可在此時建立自訂CallbackResult來對頁面中其他控制項進行修改,最後將這些
CallbackResults賦給正確的控制項即可;響應帶回用戶端後,由Web ADF
Javascript的processCallbackResult()函數進行解析。:
下載 (34.4 KB)2010-1-31 00:59
- 情境二:非Web ADF控制項來產生請求和響應,用戶端processCallbackResult()來處理響應
比如頁面實現了ICallbackEventHandler介面,請求由頁面中的一個HTML
Button發出,在RaiseCallbackEvent()方法中將所有響應,也包括非Web
ADF控制項的,都構建成CallbackResult的結構,並集中到CallbackResultCollection中,然後在
GetCallbackResult()方法中返回CallbackResultCollection.ToString(),用戶端處理
callback響應的js函數指定為Web ADF
Javascript中的processCallbackResult()函數,則省去了自己解析callback響應的麻煩。:
下載 (38.13 KB)2010-1-31 00:59
- 情境三:非Web ADF控制項來產生請求和響應,用戶端自己處理響應
除了在用戶端使用自訂的函數來解析包括CallbackResult結構在內的伺服器響應外,其餘和情境二是相同的。這種情況下你需要知道
CallbackResult的JSON字串中,使用“:::”分隔字元來分隔參數,使用“^^^”分隔字元來分隔多個CallbackResult,此外
processCallbackResult()的定義可在X:\Inetpub\wwwroot\aspnet_client\ESRI\WebADF
\JavaScript\ESRI.ADF.System.debug.js中找到。除非你已經有一套處理callback響應的js架構,否則還是推薦
在用戶端使用processCallbackResult()來進行響應處理。:
下載 (47.53 KB)2010-1-31 00:59
如果採用了ASP.NET AJAX方式:
- 情境一:Web ADF控制項觸發partial postback
此情形和Client Callback方式情境一類似,發送請求的Web ADF控制項是ASP.NET AJAX中的trigger,它的CallbackResults註冊為ScriptManager的一個data item返回用戶端。如所示:
下載 (38.41 KB)2010-1-31 00:59
- 情境二:非Web ADF控制項觸發partial postback
trigger是非Web
ADF控制項時,更新Web ADF控制項內容的方法有三種:UpdatePanels,data items或dynamic script
blocks。如果將Web ADF控制項放入UpdatePanel,則根據UpdatePanel的特性,會重新整理裡面整個Web
ADF控制項,這是不必要的,Web
ADF控制項所需要更新的內容僅僅是伺服器端產生的CallbackResults,由用戶端的processCallbackResult()即可完成,
而不需要從頭到尾的洗心革面。所以CallbackResults可以當做data items或dynamic script
blocks來插入到partial postback的響應中。
作為dynamic script blocks時:首先要使用dynamic script
blocks,頁面中至少要有一個UpdatePanel(空的也可)。只需要在用戶端調用
ESRI.ADF.System.processCallbackResult()
來處理伺服器端產生的CallbackResults(作為該函數的參數),如下:
- . . .
- string jsProcessCallbackResult = string.Format("ESRI.ADF.System.processCallbackResult('{0}');",
- Map1.CallbackResults.ToString().Replace("\\", "\\\\"));
- ScriptManager.RegisterClientScriptBlock(Page, sender.GetType(), "changeextent",
- string.Format(jsProcessCallbackResult, Map1.CallbackResults), true);
複製代碼
此方式的優點:Web ADF控制項不需要完全重新整理,用戶端響應解析也不需要自己來寫;缺點:註冊的dynamic script blocks會儲存在用戶端記憶體中,最終可能會導致瀏覽器記憶體超出限制。
作為data items時:ScriptManager中註冊的data
items可以在伺服器上以(JSON)字串形式打包傳到用戶端進行處理。用戶端接收到伺服器端的非同步postback響應後,但在進行任何局部內容的
修改前,會觸發用戶端的pageLoading事件。這樣就可以利用PageRequestManager捕捉到這個事件,從而將註冊成data
items的CallbackResults交給processCallbackResult() 來處理。過程如所示:
下載 (45.61 KB)2010-1-31 00:59
在伺服器端將CallbackResult註冊成data item:
- ScriptManager1.RegisterDataItem(Page, Map1.CallbackResults.ToString(), false);
複製代碼
監聽pageLoading事件:
- <script>
- Sys.Application.add_init(onInitFunction);
-
- // Called once during application initialization
- function onInitFunction()
- {
- Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(AsyncResponseHandler);
- }
- // Called whenever a response to a partial postback is processed on the client
- function AsyncResponseHandler(sender, args)
- {
- var dataItems = args.get_dataItems();
- if (dataItems['__Page'] != null)
- ESRI.ADF.System.processCallbackResult(dataItems['__Page']);
- }
- </script>
複製代碼
現在你對.NET ADF中的AJAX還有疑問嗎?如果有不要猶豫,請繼續閱讀以下學習資源,之後歡迎進行交流。
論壇中S-H-G朋友的系列教程:
ArcGIS Server9.3 AJAX系列(一)之CallbackResults
ArcGIS Server9.3 AJAX系列(二)之Client CallBack解決方案
ArcGIS Server9.3 AJAX系列(三)之ASP.NET AJAX解決方案
ArcGIS Server9.3 AJAX系列(四)之總結
論壇中Jueery版主的系列教程:
ArcGIS Server .Net ADF中的AJAX(一)(文章丟失,可在社區3周年精華文集中找到)
ArcGIS Server .Net ADF中的AJAX(二)
ArcGIS Server .Net ADF中的AJAX(二)續
ArcGIS Server .Net ADF中的AJAX(三)
關於Client Callback的詳解:
carlbiao朋友的新手講解.net的callback機制
ASP.NET 2.0's Client Callback Feature
關於ASP.NET AJAX:
AJAX : The Official Microsoft ASP.NET Site
關於XMLHttpRequest:
http://www.w3school.com.cn/php/php_ajax_xmlhttprequest.asp
最後不能少的就是幫AGS助中的文章,一切細節都在其中:
AJAX and ASP.NET
AJAX Capabilities of Web ADF Controls Overview
Working with CallbackResults
ASP.NET Callback Solutions
ASP.NET AJAX partial postback solutions