Rob Howard
在我的小時候,我每年都會花幾周的時間呆在我的大家庭裡。作為一個年幼的美國小男孩我當時非常著迷荷蘭電力火車,一些在我的家鄉--德克薩斯州達拉斯所沒有見到過的事情。我的堂兄招待我乘坐他們的小船去看經過的火車。坐在靠近車軌的水上可以聽到漸近的火車,像是輕輕的口哨穿過鐵軌,直到火車呼叫而過時奏出一個緩慢的高潮。在考慮ASP.NET 2.0時使我不由想起了這個。ASP.Net 2.0已經十分接近而且我們大部分都在熱切期望它的發布能早點到來,我們甚至聽到了發布繼續越來越響的“嗚”聲。屆時我們編寫軟件的方式將會改變,再一次。
微軟 ASP.Net 2.0的目標是提供開發者的效能50%。然而,實際的效能提升似乎超過了預期。新的personalization, membership和角色管理特性拿掉了開發者的負擔,而其他的特性,像數據綁定,也被簡化了。例如,我們熟悉並依然被支持的語法:
<%# DataBinder.Eval (Container.DataItem, "FirstName") %>
在ASP.Net 2.0 卻可以簡化為:
<%# Eval("FirstName") %>
不僅有大量的另人印象深刻的新特性,還有大量的極具意義的服務器控件。由於服務器控件像<ASP:login>控件對membership的整合和新的data source和數據控件服務器控件,ASP.NET編程模型將在ASP.Net 2.0中變得更加強大。
在ASP.NET 2.0中System.Web類庫的數量幾乎翻倍--覆蓋太多甚至需要雜志專欄連載。為了真正理解這些改變的程度,您需要一本新的ASP.NET 2.0的書。我計劃在這撰寫一些專欄來突出一些比較重要的ASP.Net 2.0的新特性。本月刊我將重點放在導航和頁面流程,以眾人尋求的特性--提交給其它頁面能力作為開始。
跨頁投遞
我從遷移到ASP.NET開發者們那裡聽到抱怨最多的是頁面的回發模型,ASP.Net頁面可以擁有單一的<form>並且只能HTTP回發給自己,這樣所有的處理邏輯都將運行在這個頁面裡。
許多開發者,特別是那些熟悉ASP,喜歡控制<form>元素的,會了解ASP中可以指示<form>將自己的內容數據提交到何處和如何發送(HTTP Post 或 HTTP Get),以及同一頁面上<form>的數量。但與ASP相比,ASP.NET中僅允許頁面只有一個<form runat=server>,並且只能回發給自己。這點可能令人十分懊惱,下面是ASP.Net2.0中發送給其他頁面的示例:
<%@ Page MasterPageFile="~/Site.master" Language="C#"
CodeFile="Source.ASPx.cs"
Inherits="Source" %>
<ASP:Content ID="MainContent"
ContentPlaceHolderID="Main" Runat="server">
Enter your name:
<ASP:TextBox ID="NameBox" Runat="server"></ASP:TextBox>
<ASP:Button ID="Button1" Runat="server" Text="Submit" />
</ASP:Content>
Master Pages用來控制頁面布局,帶有一個<ASP:content>塊,有一些服務器控件來接受用戶輸入。
如果打算將內容傳遞到另一個頁面,可以采用類似下面的服務器代碼:
Response.Redirect("Target.ASPx?Name= " +
HttpUtility.UrlEncode(NameBox.Text));
這種技術的問題是當用戶點擊按鈕提交後,服務器接受請求並發送一個響應令浏覽器轉向Taget.ASPx,這樣簡單的問題卻做了大量的工作!
能不能簡化工作呢?在ASP.Net 2.0中答案是肯定的。接下來演示改進後的代碼:
<%@ Page MasterPageFile="~/Site.master" Language="C#"
CodeFile="Source.ASPx.cs"
Inherits="Source" %>
<ASP:Content ID="MainContent"
ContentPlaceHolderID="Main" Runat="server">
Enter your name:
<ASP:TextBox ID="NameBox" Runat="server"></ASP:TextBox>
<ASP:Button ID="Button1" Runat="server" Text="Submit"
PostBackUrl="~/Target.ASPx" />
</ASP:Content>
注意<ASP:Button>中的PostBackUrl屬性,這個屬性會通知按鈕不再執行默認的回發而是直接提交數據到Target.ASPx.
您可能想知道這是如何運作的,尤其您熟悉ASP.Net的VIEwState對象時。不過那超出了本文的范圍,當跨頁投遞特性被使用時頁面會新增一個隱藏域:
<input type="hidden" name="__PREVIOUSPAGE" id="__PREVIOUSPAGE"
value="p1-dFHlCpgH2alr1vkr3G21UIR7jOuzn074led6lbGf1KQ47_F25GwG0" />
有點像控件樹所生成的視圖狀態,但它是給跨頁投遞驗證此頁的視圖狀態的碎片。您知道,當一個頁面被跨頁投遞給另一個頁面後,接受頁要可以訪問投遞頁的實例。本例中意味著Target.ASP可以訪問Source.aspx的詳細信息。事實上,更有效的方法是通過強類型管理器訪問Source.aspx到Target.aspx的API。為了訪問投遞頁(上個頁面),ASP.Net2.0特別為跨頁投遞提供一個頁面屬性:PreviousPage。
PreviousPage返回投遞頁的實例,還有一個屬性是用來檢查是否是跨頁投遞:IsCrossPagePostBack。這個屬性類似現有的IsPostBack但是只有跨頁投遞發生時返回true。
PreviousPage屬性可以有不同的行為,默認只簡單將上個頁面的實例作為Page類型返回,但是,通過使用一個新的指令您可以讓PreviousPage屬性返回一個強類型實例,來訪問頁面的public成員。例如,將下面的代碼添加到Target.ASPx上:
<%@ PreviousPageType VirtualPath="~/Source.ASPx" %>
現在可以在Target.aspx上使用PreviousPage屬性來訪問Source.aspx的數據了。然而,為了訪問服務器控件,像Source.ASPx上的NameBox,您還需要編寫以下代碼:
TextBox nameBox = PreviousPage.FindControl("NameBox") as TextBox;
換句話說,您必須使用FindControl來訪問控件樹。為什麼呢?服務器控件默認作為受保護成員變量,為了真正地簡單訪問上個頁面的元素,您需要將Source.ASPx上的屬性或方法顯露為public,然後下面的代碼才能工作: TextBox nameBox = PreviousPage.NameBox;
跨頁投遞是ASP.NET非常棒的一個特性,有一些文章深入討論了跨頁投遞的技術細節,如果有興趣跨頁投遞是如何運作的,可以查看Dino Esposito在MSDN雜志9月刊的 Cutting Edge column(請見本人譯文:ASP.NET 表單 (翻譯))。您很可能也會發現,如果您很精通ASP.Net,那麼您大多數時候仍會繼續使用標准的頁面回發模型。(譯者:言外之意,您如果是高手的話會不屑於此特性)
向導控件
通過跨頁投遞,可以很容易的給應用程序構建一個復雜的導航功能。然而,這個特性卻不能簡化您構建向導樣式的用戶界面。為了完成任務,無論線形或非線形,向導樣式的用戶界面都經常被設計。它提供給終端用戶一個親切的途徑完成一系列復雜的步驟,每個步驟都被打碎為很多塊。
在ASP.NET 1.x,向導常通過一些技巧來實現:將多個<ASP:panel>服務器控件放在同一個頁面,通過用戶所在的位置切換可見性。在ASP.Net中編寫一個向導不是一件易事,很多設計師的丟棄了向導,並且步驟流程的管理也很混亂。
ASP.NET中新的跨頁投遞能力可以用來解決向導問題,但是當需要非線性導航存在時就同樣是挑戰了。例如,步驟1,步驟2,跳過步驟3-5,步驟6,步驟3,步驟2,步驟6,ASP.Net 2.0 向導控件解決了大部分這類問題。還有,通過頁面回發模型替換跨頁投遞向導的所有的輸入元素可以持續訪問。
向導控件功能上很大程度的接近ASP.Net 1.1中隱藏panel的方式。然而,向導控件顯露了一系列的<ASP:WizardStep>,它們可以包含任意數量的子控件,然而每個<ASP:WizardStep>需要有自己的unique ID,見圖1。向導控件頁管理所有的導航,支持線性和非線性導航,並且有vs完整的設計時支持。圖2演示了向導控件,左邊是基於link的非線性導航,右下是線性的按鈕導航。從打開的任務菜單,可以看到不僅僅是一個公共任務而是一個步驟列表允許在設計時切換步驟。
圖2 向導在Visual Studio中的情形
向導控件的所有可見元素都是可以配置的。非線性鏈接可以用按鈕或刪除入口代替,上一步,下一步,完成線性導航元素也可以改變為圖片按鈕或鏈接。事實上,控件所有的面貌都可以通過模板配置。
在ASP.Net 1.1中編寫向導控件的一個難點就是管理用戶應該身在何處。向導控件通過顯露ActiveStep屬性簡化了此工作。ActiveStep屬性可以查詢並確定當前哪個步驟是被激活的。向導的自然流程會按照實現聲明好的方式進行,流程可以通過MoveTo方法隨時改變。通過MoveTo,任意步驟可以被設定為ActiveStep,為了輔助導航和流程,還提供了幾個事件,見圖3。
新的向導控件在搜集用戶信息時非常有用,不想ASP.NET 1.1中所有的基礎構造都要編寫,ASP.NET把所有的工作都給您准備好了。向導控件太有用了,ast.Net小組使用它作為CreateUserWizard控件的基類,CreateUserWizard控件用作Membership功能的一部分創建用戶。
小節
跨頁投遞和<ASP:Wizard>控件給ASP.Net開發者在應用程序中控制導航流程帶來幾個新的選擇。跨頁投遞對於目前需要使用Response.Redirect 或 Server.Transfer 情況非常有用。向導控件用來構建同時需要線形和非線形的復雜的數據搜集的確很棒。
------------------------------------------------
圖 1 向導步驟
<ASP:Wizard runat="server" >
<WizardSteps>
<ASP:WizardStep ID="Step1">
Welcome!
</ASP:WizardStep>
<ASP:WizardStep ID="Step2">
What is your name: [TextBox1]
[Button1]
</ASP:WizardStep>
<ASP:WizardStep ID="Step3">
Thank you, [TextBox1.Text]!
</ASP:WizardStep>
</WizardSteps>
</ASP:Wizard>
-----------------------------------------------------
圖 3 Navigation Events