ASP.NET入門隨想之服務器控件模型
他迷上了珍珠奶茶。說實話,以前他一直覺得珍珠奶茶挺蠢的,真不知道是那一個天才的點子,居然把粉圓和泡沫紅茶攪和在一起,還加上一根那么滑稽的超大吸管;妹妹剛迷上珍珠奶茶的時候,他還挺嗤之以鼻的呢。但這天以后,他幾乎天天都會去買一杯珍珠奶茶,愈喝愈覺得珍珠奶茶的風味很特別,的確滿好喝的。知道他迷上珍珠奶茶,妹妹還笑他:"早跟你說好喝你不信,男生就是遲鈍。"
-- 管家琪《珍珠奶茶的誘惑》
■ 面向對象的粉圓 - 服務器控件模型
《隨想九》中我們已經認識到,XHTML的一個重要目標就是將結構與表現分離,如:<div id="author">老燕</div>;但從一個動態網站的角度來看,需要進一步將網頁結構與內容分離,即:<div id="author"><!--動態數據--></div>,相比較而言,網頁結構是靜態,內容是動態。
charly的《內容發布系統的開發》引發我對一段往事的追憶,作者討論的命題是如何用動態數據生成靜態頁面,方法是用數據替換模板中指定標簽。引申開來就是ASP程序員非常久遠的夢想--代碼與頁面分離,即避免出現<div id="author"> <%=author%></div>之類HTML代碼與ASP程序代碼混雜在一塊的情況,,而解決之道,用的就是charly描述的方法--正則表達式替換。
如果用靜態/動態這個角度去分析一個.aspx文件,可以將其分成兩部份:一部份是靜態的連續的文本,如:<html>……<body>;另一部份是動態的特殊標簽,如:<asp:TextBox id="txtName" runat="server" />。兩者以是否擁有屬性runat="server"為判斷標準。ASP.NET將后一部份稱之為服務器控件,程序員以服務器控件為對象模型來定義Web應用程序的用戶界面,控制用戶交互;而前一部份在運行時也將被創建成一種特殊的控件--LiteralControl。
如果用ASP面向過程的方法來處理所謂的服務器控件,就是根據用戶的需求直接生成對應的HTML代碼;而在ASP.NET中,程序員與HTML代碼被抽象的面向對象的服務器控件概念隔離開來。既然面向對象,服務器控件就應該擁有屬性(property)來描述自己的狀態;用方法(method)描述自己的動作;需要事件(event)來觸發方法,改變狀態,最后自動生成相應的HTML代碼。
當然我們不需要從頭來構架這個模型,所有的服務器控件,包括Page類,都直接或間接繼承于System.Web.UI.Control類,而顯示為HTML表單元素的控件,往往又繼承于System.Web.UI.WebControl類,稱為Web控件。下例是一個簡單的自定義控件范例,訪問該例的TestMyControls.aspx頁,查看源代碼會發現控件對應的Hhtml代碼為"1"。
■ 葛玲是誰? - 服務器控件的狀態保持
若干年前有一個火腿腸廣告,對話如下:
呂麗萍:冬寶,在想啥呢?
葛 優:想葛玲
呂麗萍:別想了,我給你介紹一位新朋友--DUDU牌火腿腸
呂麗萍:(過一會兒)還想葛玲嗎?
葛 優:葛玲是誰?
人機交互設計的一個重要內容是交互工作流,而實現交互工作流的前提是狀態保持,否則就會出現"葛玲是誰"這樣的幽默。
控件可以利用傳統的cookies、session、隱藏控件等方法來存儲狀態值,在《隨想八》中我們已經探討過視圖狀態(ViewState)的作用和原理,本質上,ASP.NET創造出的有狀態、連續的頁面狀態保持機制是通過頁面隱藏數據。接下來我們通過改造上例進一步研究利用視圖狀態來完成控件狀態保持的細節。
■ 換杯子還是換粉圓? - 控件樹與服務器控件的生命周期
在運行期,頁面框架會在杯子里放入指定的服務器控件類實例,當然它們不是胡亂堆積在一塊,而是組合成一顆控件樹,圖10-2為例2頁面的控件樹模型,我們可以通過控件的ID或在樹中的位置控制控件,也可以增加或刪除控件。
在假想的有狀態、連續的頁面前提,意味著在初始請求后,頁面必須保存每一個控件的狀態,在回傳(PostBack)后,首先是恢復控件原來的狀態,再處理新的請求。也就是說,每一個客戶端對同一個頁面的連續N次請求,相當于向奶茶鋪連續要了N杯同一品種的奶茶,第一杯珍珠奶茶中粉圓狀態是默認的,從顧客提出第二杯奶茶請求起,伙計必須先把這杯中粉圓狀態撥弄成與端給顧客時的上一杯狀態一模一樣,然后根據顧客新的事件進行調整。如圖10-2。詳細過程請查閱MSDN的《控件執行生命周期》。
這種無中生有的交互工作流實現方法是要付出代價的,作為編程模型而言,程序員可以在服務器控件的抽象概念上輕松實現,但對整個系統的性能而言,ASP.NET并未對客戶端和服務器端做過多少負載性能優化,用合金槍頭(我的一位網友)的話來說,"如果就連選擇籍貫省市的下拉框都要用服務器控件來提交一下,同時刷新頁面的話,那的確很惡心"。
一個很自然的想法是為什么對每一次請求我們都要把整個杯子換掉呢?如果僅僅是換掉幾個粉圓和一小部份奶茶,將大大改善系統性能,這就是Ajax技術的出發點。
-- 管家琪《珍珠奶茶的誘惑》
■ 面向對象的粉圓 - 服務器控件模型
《隨想九》中我們已經認識到,XHTML的一個重要目標就是將結構與表現分離,如:<div id="author">老燕</div>;但從一個動態網站的角度來看,需要進一步將網頁結構與內容分離,即:<div id="author"><!--動態數據--></div>,相比較而言,網頁結構是靜態,內容是動態。
charly的《內容發布系統的開發》引發我對一段往事的追憶,作者討論的命題是如何用動態數據生成靜態頁面,方法是用數據替換模板中指定標簽。引申開來就是ASP程序員非常久遠的夢想--代碼與頁面分離,即避免出現<div id="author"> <%=author%></div>之類HTML代碼與ASP程序代碼混雜在一塊的情況,,而解決之道,用的就是charly描述的方法--正則表達式替換。
如果用靜態/動態這個角度去分析一個.aspx文件,可以將其分成兩部份:一部份是靜態的連續的文本,如:<html>……<body>;另一部份是動態的特殊標簽,如:<asp:TextBox id="txtName" runat="server" />。兩者以是否擁有屬性runat="server"為判斷標準。ASP.NET將后一部份稱之為服務器控件,程序員以服務器控件為對象模型來定義Web應用程序的用戶界面,控制用戶交互;而前一部份在運行時也將被創建成一種特殊的控件--LiteralControl。
如果用ASP面向過程的方法來處理所謂的服務器控件,就是根據用戶的需求直接生成對應的HTML代碼;而在ASP.NET中,程序員與HTML代碼被抽象的面向對象的服務器控件概念隔離開來。既然面向對象,服務器控件就應該擁有屬性(property)來描述自己的狀態;用方法(method)描述自己的動作;需要事件(event)來觸發方法,改變狀態,最后自動生成相應的HTML代碼。
當然我們不需要從頭來構架這個模型,所有的服務器控件,包括Page類,都直接或間接繼承于System.Web.UI.Control類,而顯示為HTML表單元素的控件,往往又繼承于System.Web.UI.WebControl類,稱為Web控件。下例是一個簡單的自定義控件范例,訪問該例的TestMyControls.aspx頁,查看源代碼會發現控件對應的Hhtml代碼為"1"。
// MyControls.cs 自定義控件集 using System; using System.Web.UI; namespace essay{ public class MyFirstControl:Control{ //輸出屬性Number絕對值控件 private int _number; public int Number{ //Number定義屬性 get {return _number;} set {_number=value;} } //重寫Control.Render方法,生成控件對應的HTML代碼 protected override void Render(HtmlTextWriter writer) { writer.Write(Math.Abs(Number)); } } } // TestMyControls.aspx頁面文件,<%Register%>注冊自定義控件集 // <mc: ……>在頁面增加自定義控件并將屬性Number值設為-1 <%@ Register TagPrefix="mc" Namespace="essay" Assembly="essay" %> <HTML><HEAD></HEAD><body> <form runat="server"> <mc:MyFirstControl id="test1" Number="-1" runat="server" /> </form></body></HTML> |
■ 葛玲是誰? - 服務器控件的狀態保持
若干年前有一個火腿腸廣告,對話如下:
呂麗萍:冬寶,在想啥呢?
葛 優:想葛玲
呂麗萍:別想了,我給你介紹一位新朋友--DUDU牌火腿腸
呂麗萍:(過一會兒)還想葛玲嗎?
葛 優:葛玲是誰?
人機交互設計的一個重要內容是交互工作流,而實現交互工作流的前提是狀態保持,否則就會出現"葛玲是誰"這樣的幽默。
控件可以利用傳統的cookies、session、隱藏控件等方法來存儲狀態值,在《隨想八》中我們已經探討過視圖狀態(ViewState)的作用和原理,本質上,ASP.NET創造出的有狀態、連續的頁面狀態保持機制是通過頁面隱藏數據。接下來我們通過改造上例進一步研究利用視圖狀態來完成控件狀態保持的細節。
// MyControls.cs 自定義控件集 …… public class MyFirstControl:Control{ //輸出屬性Number絕對值控件 private int _number; public int Number{……} //增加屬性NumberInViewState,用以存取屬性Number的視圖狀態值 public int NumberInViewState{ get{ object o = ViewState["NumberInViewState"]; return (o==null)?0:(int)o; } set { ViewState["NumberInViewState"]=value; } } protected override void Render(HtmlTextWriter writer){……} } …… // TestMyControls.aspx頁面文件 <html> …… <%@ Page Language="C#" %> <script runat="server"> protected override void OnLoad(EventArgs e) { test1.NumberInViewState--; //視圖狀態相應值自減1 test1.Number = test1.NumberInViewState; //自定義控件值與視圖值保持一致 base.OnLoad(e); } </script> <form runat="server"> <mc:MyFirstControl id="test1" runat="server" /> <input type="submit" /> </form></body></html> |
■ 換杯子還是換粉圓? - 控件樹與服務器控件的生命周期
在運行期,頁面框架會在杯子里放入指定的服務器控件類實例,當然它們不是胡亂堆積在一塊,而是組合成一顆控件樹,圖10-2為例2頁面的控件樹模型,我們可以通過控件的ID或在樹中的位置控制控件,也可以增加或刪除控件。
在假想的有狀態、連續的頁面前提,意味著在初始請求后,頁面必須保存每一個控件的狀態,在回傳(PostBack)后,首先是恢復控件原來的狀態,再處理新的請求。也就是說,每一個客戶端對同一個頁面的連續N次請求,相當于向奶茶鋪連續要了N杯同一品種的奶茶,第一杯珍珠奶茶中粉圓狀態是默認的,從顧客提出第二杯奶茶請求起,伙計必須先把這杯中粉圓狀態撥弄成與端給顧客時的上一杯狀態一模一樣,然后根據顧客新的事件進行調整。如圖10-2。詳細過程請查閱MSDN的《控件執行生命周期》。
![]() |
這種無中生有的交互工作流實現方法是要付出代價的,作為編程模型而言,程序員可以在服務器控件的抽象概念上輕松實現,但對整個系統的性能而言,ASP.NET并未對客戶端和服務器端做過多少負載性能優化,用合金槍頭(我的一位網友)的話來說,"如果就連選擇籍貫省市的下拉框都要用服務器控件來提交一下,同時刷新頁面的話,那的確很惡心"。
一個很自然的想法是為什么對每一次請求我們都要把整個杯子換掉呢?如果僅僅是換掉幾個粉圓和一小部份奶茶,將大大改善系統性能,這就是Ajax技術的出發點。