ASP.NET的IScriptControl
在基本的ASP.NET AJAX框架下,我們有三種方法來做基于Control的Ajax操作,它們分別是UpdatePanel、ICallbackEventHandler和IScriptControl,下面我們就分別看看它們的特點和使用場景。
UpdatePanel
UpdatePanel是與服務器端邏輯進行交互的多種方案中最易用的一個,甚至就不能稱之為交互——你根本就不需要觸及任何客戶端邏輯。一個服務器端操作,經過UpdatePanel的“劫持”,變成了一個客戶端操作,而這個客戶端操作又直接調用對應的服務器端操作,就這么簡單。
如果用UpdatePanel來做一個帶分支的選擇對話框,那應該如何設計?思路可別跑到客戶端的confirm方法上去,那可太繞了,或者說太不ASP.NET AJAX了。用UpdatePanel,就應該堅持它的理念,一切客戶端操作都是幻象,所有操作其實都是在服務器端進行的,包括選擇對話框。要按ASP.NET的思路來做,我會做一個選擇對話框控件,它的實質可能是一個浮動層模擬的對話框,這屬于實現細節,我們不用太關注。重點是,這個選擇對話框的分支邏輯是完全在服務器端進行的,Async PostBack之后服務器端根據提交回來的數據決定如何觸發事件。這樣做整個分支選擇的邏輯就是內嵌在Page處理流程當中的,不需要通過 Cookies或者Session來做數據的中轉媒介,避免了Page處理流程與更大作用域中的數據的緊耦合。
UpdatePanel適用于邏輯完全在服務器端的開發,并且我建議使用UpdatePanel時也就把所有邏輯放在服務器端,不要去寫一些混合服務器端邏輯與客戶端邏輯的代碼。有人會說,你看老趙就很喜歡去動那個Sys.Net.WebRequestExecutor 來改變UpdatePanel的行為啊,但其實這屬于分層設計思想中的一部分,他去動那個東西改變的也就是一個分層內的邏輯,只要層與層之間的接口不變,具體實現是可以按需設計的。但如果你用了UpdatePanel,同時又用Cookies或者Session來傳值,這就跨越了n個層,增加了不少耦合度。
ICallbackEventHandler
關于ICallbackEventHandler,我已經說過無數次了,重點還是你必須用Page處理流程來思考,只要你理解了Page處理流程,你就明白為什么ICallbackEventHandler在。NET Framework 2.0 Beta2中只有一個方法,而到了RTM要分拆成兩個方法。具體可以參考《ASP.NET 2.0 ClientScript Callback》,我就不再重復了。
如果用ICallbackEventHandler實現一個帶分支的選擇對話框,又如何做?和使用UpdatePanel的做法類似,我還是會做一個選擇對話框控件,并且這個控件繼承自ICallbackEventHandler.為這個控件編寫JavaScript并實現ICallbackEventHandler接口時,我會確保JavaScript 對Callback給出正確的調用參數,并在接口方法的實現中接收這些參數然后觸發正確的事件,就這么簡單。和UpdatePanel一樣,不要偏離了 ICallbackEventHandler的設計思想,它的處理流程必須是合并到Page處理流程中的,你的控件也就必須這樣設計。
至于在什么情況下選擇ICallbackEventHandler?如果你有一個輕量級的Ajax操作,但使用UpdatePanel更新整個區域的 HTML開銷很大的話,那么你可以考慮使用ICallbackEventHandler.當然,前提是你懂得控件開發和JavaScript.
IScriptControl
這是最復雜的解決方案了,你需要實現一個Control的兩個副本——一個服務器端的,一個客戶端的。有一部分邏輯,是要在客戶端和服務器端重復實現兩次的,而另外一部分邏輯,只需要在客戶端或服務器端之中的一個實現一次。IScriptControl的經典例子,當然是ASP.NET AJAX自帶的Timer控件。它的計時器是純粹的客戶端邏輯,然而Tick事件卻在服務器端觸發,Async PostBack成為了兩者之間的橋梁。
當然,就Control本身而言,它并不在乎PostBack是不是異步的,Tick事件只因PostBack而觸發。
如果用IScriptControl來實現帶分支的選擇對話框,那將會和ICallbackEventHandler的版本十分相似,唯一不同的地方就是它在客戶端的邏輯會被封裝為一個Sys.UI.Control的派生類,而ICallbackEventHandler的客戶端邏輯往往是不封裝的。這樣的好處顯而易見,那就是代碼更容易維護了,并且客戶端的Control可以同樣可以加入事件支持,并提供和服務器端一樣的代碼分支事件。要知道在CTP階段的Timer控件,其客戶端版本Sys.Timer(而非RTM的Sys.UI._Timer)是擁有tick事件的,和服務器端的Tick事件對應,只不過RTM取消了此項功能,因為ASP.NET AJAX 1.0的側重點完全就是服務器端功能,客戶端功能都被砍掉了。
什么情況下選用IScriptControl?如果你認為你的客戶端邏輯應該封裝為Sys.UI.Control的派生類,那就選擇IScriptControl吧。
小結
我們分別討論了三種通過Control實現Ajax調用的方案,并且一再強調了設計必須基于Page處理流程,不要在此流程之外增加不必要的復雜度和耦合度。值得一提的是,有很多人質疑為什么要在Web上提供這樣一個支持分支的選擇對話框功能,我的看法是這樣的:既然客戶端軟件的流程會有此功能,那么Web應用也有此功能就實在是太正常了,你刪除blog post的時候問你一下是否確認刪除,難道會有人覺得這個功能是設計錯誤?可能不同的只是表現形式而已,到底是confirm還是彈出層,甚至是一個專用的過渡頁面。然而從用戶體驗的角度來說,這其實并不是***的方案,多數時候用戶刪除就是確認刪除,并不需要再問一次是否確認之類的愚蠢問題,但開發人員覺得用戶錯手刪除的后果應當由用戶自己承擔,所以就做了這樣一個對話框來推卸責任。真正好的用戶體驗是不需要確認的刪除,但用戶一定能夠恢復,***是按一下 Ctrl+Z就可以了,然而對于開發人員來說還是有很多操作是無法做到可恢復的,這時候除了顯示對話框也沒有更好的解決方案了。
【編輯推薦】