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
Event
Description
ActiveStepChanged
Raised when the ActiveStep is set to a new WizardStep
CancelButtonClick
Raised when the button identified as the Cancel button is clicked
FinishButtonClick
Raised when the button identified as the Finish button is clicked
NextButtonClick
Raised when the button identified as the Next button is clicked
PreviousButtonClick
Raised when the button identified as Previous button is clicked
SideBarButtonClick
Raised when one of the SideBar links or buttons is clicked