单窗体界面(Single-Form Interface)
在SFI模型中,每个页面总是投递给自己,而没有为开发者设置最终回发目的地的接口。
从ASP.NET 2.0版开始,可通过“跨页投递API”向其他页面投递窗体数据。
与HTML和ASP编程中窗体的Action属性使用URL不同,HTTP方法和回发的目标框架能够通过HtmlForm属性以编程方式确定。
HtmlForm类
HtmlForm的父类HtmlContainerControl为窗体提供了容纳子控件的功能,该功能也由其他可包容子元素且带有结束标签特性的HTML类(如HtmlTable)共享。
HtmlForm类提供了在服务器端以编程方式访问HTML中<form>元素的功能,所涉及如下表所示:

窗体必须有自己唯一的名称,如果程序员没有指定该名称,ASP.NET会通过内建的算法生成一个。默认的名称遵循CtlX模式,X代表唯一的整型值。程序员通过ID和Name属性都可设置窗体的标识符,如果同时设置,最终会采用ID属性的值。
窗体的父对象为带有runat属性的外层容器控件,如果不存在这样的对象,那么页面对象就会被设置为窗体对象的父对象。
在默认情况下,Method属性会被设置为POST。该属性值能够以编程方式进行修改。如果窗体是通过GET方法回发的,所有窗体数据会通过URL的查询字符串传递。但使用GET方法,应该确保GET请求的长度不会影响应用程序的完整性,也应确保其不会引发安全问题。
虽然RFC2068未规定URL的最大或最小长度,但浏览器对URL的大小一般都有限制,从而影响带有查询参数URL的GET请求长度。在Win平台上,Opera浏览器的上限为4000个字符,IE不能超过2KB。一般来讲,2KB是一个重要的上限指标。
HtmlForm类中常用的方法:

需要注意的是:FindControl方法只搜索当前窗体的直属子控件,命名容器内部的控件或窗体子控件的子控件都不在搜索范围内。
多窗体
虽然ASP.NET不允许在同一页面中包含多个带有runat属性的<form>标签,但在一个页面中包含一个带runat属性的<form>标签和多个不带runat属性的<form>标签是完全被允许的。示例代码:
<body>
<table>
<tr><td>
<form id="form1" runat="server">
......
</form>
</td></tr>
<tr><td>
<form method="post" action="search.aspx">
......
</form>
</td></tr>
</table>
</body>
以上代码能正常运行,但有一个严重缺陷:不能使用ASP.NET编程模型获取客户端活动页面中被投递的数据。必须使用过去的ASP模型中的方法来获取被投递数据。
如果在Web窗体中声明多个服务器窗体,将会抛出异常。但有一个大多数人不知的技巧:只要同一时刻Web窗体中只有一个可见的服务器端窗体,我们便可包含任意多个窗体。如:允许一个页面中包含3个<form runat=server>标签,但只能有一个窗体被呈现。
示例代码:
<body>
<form id="step0" runat="server" visible="true">
......
</form>
<form id="step1" runat="server" visible="false">
......
</form>
<form id="step2" runat="server" visible="false">
......
</form>
</body>
我们可以在服务器代码中修改各form的Visible属性,这样便可动态控制页面显示哪个form了。
如果编写ASP.NET 2.0或ASP.NET 3.5应用程序,有两种新控件可代替以上的技巧:MultiView和Wizard。MultiView控件的运行逻辑几乎与互斥窗体相同,但它利用的是面板,而不是完整的窗体。
跨页投递
实现向其他页面投递数据的Web页面,需要做两项工作:
1. 选出执行回发的控件,并设置其PostBackUrl属性。示例代码:
<form id="Form1" runat="server">
<asp:textbox runat="server" id="Data" />
<asp:button runat="server" id="buttonPost" Text="Click" PostTargetUrl="target.aspx" />
</form>
<input type="submit" name="Button1" value="Click"onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("Button1","", true, "","target.aspx", false, false))"id="buttonPost" />
这样,当用户单击该按钮时,当前窗体会将其内容投递给指定的目标页面。那么视图状态如何处理呢?当页面包含用于执行跨页回发的控件时,会有一个新的隐含字段被创建--__PREVVIOUSPAGE。该字段包含处理请求所需的视图状态信息,这些状态信息可被透明的利用,取代所投递页面的原视图状态。
我们可使用PreviousPage属性来引用主动投递页面及其中控件。
示例代码:
protected void Page_Load(object sender, EventArgs e)
{
TextBox txt = (TextBox)PrevioutPage.FindControl("TextBox1");
}
@PreviousPageType指令
通过页面的@PreviousPageType指令,我们可指定主动投递页面的类型。示例代码:
<%@PreviousPageType VirtualPath="crosspagewithtype.aspx" %>
该指令可接受VirtualPath或TypeName属性(这两个属性是互斥的)。
2. 如果原页面包含自定义验证器,那么应该在源页面上编写服务器端的验证代码。
示例代码:
//源页面中的控件代码
<asp:CustomValidator ID="CustomValidator1"
runat="server" Text="*" ControlToValidate="TextBox1"
OnServerValidate="ServerValidate" />
//源页面中的后台验证代码
void ServerValidate(object source, ServerValidateEventArgs args)
{
args.IsValid = false;
if(String.Equals(args.value, "Dino"))
args.IsValid = true;
}
//目的页面中的检查验证结果
if(!PreviousPage.IsValid){Response.Write("Sorry, the original page contains invalid input.");Response.End();return;}
到其他页面的重定向
除按钮控件的PostBackUrl属性外,ASP.NET还提供另一种将控件和值传输到另一个页面的机制--Server.Transfer。
新页面的URL不会反映在浏览器的地址栏中,因为整个传输过程完全在服务器上执行。
示例代码:
protected void Button1_Click(object sender, EventArgs e)
{
Server.Transfer("target.aspx");
}
注意:在该页面中,所有在Transfer方法之后的代码都不会被执行。归根结底,Transfer只是一种重定向方法,但它十分有效,原因有二:
1. 该方法不会引发到客户端的往返过程,而Response.Redirect则将引起到客户端的往返过程。
2. 它复用了处理调用者请求的HttpApplication对象,从而降低了对ASP.NET基础架构的影响。
如何判断某个页面是以服务器传输方式被调用,还是以跨页投递方式被调用呢?在这两种情况下,PreviousPage页面都不为空,而对于跨页投递来说,PreviousPage对象的IsCrossPagePostBack为true,而在使用服务器传输的情况下则为false。
数据在页面间传递的方式有多种:跨页投递、服务器传输、HTML窗体、查询字符串。哪种方式最有效?跨页投递和服务器传输的编程模型类似,但需要通过__PREVIOUSPAGE字段移动大量数据。这些数据是否真的需要,取决于目标页面的特性。许多情况下,目标页面只需几个参数就能工作。这种情况下,就被传输的数据而言,HTML客户端窗体可能会更有效。
浙公网安备 33010602011771号