Your Ad Here
首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 编程语言 > .NET > C#.NET > 第3章 WEB窗体
【标  题】:第3章 WEB窗体
【关键字】:WEB
【来  源】:http://blog.csdn.net/z365days/archive/2006/11/20/1400247.aspx

第3章 WEB窗体

Your Ad Here

(译自 Pro ASP.NET 2.0 IN C# 2005 ,很多地方译得不好,欢迎批评指正)

3 WEB窗体

ASP.NET页面(正式名称叫Web窗体)是ASP.NET应用至关重要的一部分。它们提供了Web应用的真实输出,即客户请求网页并在浏览器里浏览。

尽管网页并不是新事物,但Web窗体却是ASP.NET独有的。本质上,Web窗体允许你Windows应用那样使用基于控件的接口来创建Web应用。为了运行ASP.NET Web窗体,ASP.NET ISAPI扩展读取整个文件,产生相应的对象,并且释放一系列事件。使用完全面向对象的代码可以对这些事件进行处理。

本章深入介绍了Web窗体。你将学习到它们的工作原理和如何使用它们来创建简单的网页。你还将深入了解页面处理的生命周期和ASP.NET服务器控件模型。

WEB窗体在.NET2.0里的变化

l         Web窗体模型在ASP.NET2.0里有了些许的调优,而不是巨大的变化。一些变化在表面是看不出来的。例如,页面在生存周期中包含了更多的事件,那样它们就可以插入其它的ASP.NET功能,如主题和新的数据绑定模型。

l         视图状态分片(View state chunking):并不将所有视图状态信息存放在单个域中,可以让ASP.NET将其存入几个大小适当的域中。这个特征主要解决代理服务器的问题,这些代理服务器不支持大块的隐含输入域。

l         XHMTL支持(XHTML support):Web窗体使用XHTML兼容的标签来进行渲染,这是从ASP.NET 1.1里移植过来的,但在.NET2.0中使用几乎不会有任何问题。

l         可编程的页头(Programmable page header):网页的<head>部分现在是HtmlHead服务器控件的一个实例。可以通过程序改变标题、添加metadata或者链接的样式表到页面。

如果你是一个经验丰富的ASP.NET 1.X开发者,在阅读本章的过程中,你会体验到这些变化。

 

页面处理(Page Processing

ASP.NET的核心思想,是创建一个模型来帮助开发者采用与Windows开发者在桌面应用中构建定做的(made-to-measure)窗口一样的方法来快速开发Web窗体。当然,Web应用与传统的胖客户端应用大大不同。有两个关键的绊脚石:

l         Web应用在服务器端执行(Web applications execute on the server):例如,假定你创建了一个窗体,允许客户选择产品记录并且更新信息。用户在浏览器中执行这些任务,但为了执行要求的操作(如更新数据库),你的程序需要在Web 服务器上执行。为了处理这个界线(divide),ASP.NET采用了回滚(postback)技术,执行一定的动作时,客户端就发送页面(和用户输入的所有信息)到服务器。一旦ASP.NET接到了页面,便触发服务器端相应的事件来通知你的程序。

l         Web应用是无状态的(Web applications are stateless):换句话说,在渲染过的HTML发送给用户之前,网页对象被销毁,所有客户端特定的信息都被丢弃。这个模型在高可扩展性和重负载的应用中表现良好,但要创建无缝的用户体验还有些困难。ASP.NET内置了几个工具来帮助克服这个障碍。最引人注目的是视图状态(view state),它是一种坚持机制(persist mechanism),能够自动在已渲染的HTML页的隐含域里嵌入页面信息。

在后面的小节中,你将会学习回滚和视图状态功能。同时,这些机制为底层HTMLHTTP细节的抽象,允许程序员根据对象和事件来进行操作提供了方便。

 

HTML窗体(HTML Forms

如果熟悉HTML,你就会知道将客户端的数据发送给服务器的最简单的方法就是使用<form>标签。在<form>标签里,你可以放置<input>标签来代表基本的用户接口(UI)要素,如按钮(button)、文本框(text box)、列表框(list box)和单选按钮(radio button))

例如,下面的窗体中有一个提交(sumbmit)按钮、两个检查框、两个文本框,一共5<input>标签:

<html>

<head>

<title>Programmer Questionnaire</title>

</head>

<body>

<form method="post" action="page.aspx">

<p>Enter your first name:&nbsp;

<input type="text" name="FirstName"/><br>

Enter your last name:&nbsp;

<input type="text" name="LastName"/><p>

<p>You program with:<br>

&nbsp;&nbsp;&nbsp;

<input type="checkbox" name="C"/>C#<br>

&nbsp;&nbsp;&nbsp;

<input type="checkbox" name="VB"/>VB .NET<br><br>

<input type="submit" value="Submit" id="OK"/>

</p>

</form>

</body>

</html>

3-1显示了这个最基本的页面的浏览结果:

一个简单的HTML窗体

3-1 一个简单的HTML窗体

当用户点击提交按钮时,浏览器收集每个控件的当前值,并且在一个长字符串里将它们粘在一起。通过HTTP POST操作,这个字符串随后被发送给<form>标签里指定的网页(在本例中是page.aspx)。

在这个例子中,Web服务器可能接收到带有该信息串的请求:

FirstName = Matthew&LastName = MacDonald&C=on&VB =on

浏览器构建这个串时遵循一些规则。信息是作为一系列名字/值对来发送的,名字与值之间有一个等号(=),多个名字/值对之间用and符号(&)来分隔。检查框在没被选定的时候会被忽略,选定后,以on作为检查框的值。关于HTML窗体标准的完整内容,请参阅http://www.w3.org/TR/REC-html40/interact/forms.html

事实上,所有服务器端的编程框架在原始窗体数据上添加了一个抽象层。它们解析这个字符串,并且以更有用的方式来使用它。例如,JSPASPASP.NET都允许你使用一个瘦对象层来获取窗体控件的值。在ASPASP.NE中,可以在Request.Form集里通过名字查寻值。在ASP.NET里的示例如下:

String firstName = Request.Form[“FirstName”];

在真实的POST信息上面构建一个瘦的装饰层(thin veneer)是非常有用的,但这还远不是面向对象框架。这也是ASP.NET进行更多改进的原因。当页面传回ASP.NET,它析取串值,组装窗体集(为了向ASP代码提供兼容),然后配置对应的控件对象,使你能够使用如下更加直观的语句来获取信息:

String firstName = txtFirstName.Text;

这段代码也增强了类型的安全。也就是说,如果你要获取检查框的状态,你将会接收到一个布尔的truefalse值,而不是单词on。这样,开发人员就可以避免编写奇怪的HTML代码。

注意,在ASP.NET中,控件放置在<form>标记中间。这个标签有runat = “server”属性,表明允许它在服务器端工作。ASP.NET不允许创建多个服务器端的窗体标签,尽管可能使用交叉传递技术来创建向另一个页面传递信息的网页,第6章对此有详细介绍。

 

动态接口(Dynamic Interfaces

显然,控件模型使获取窗体信息更为方便,但更为引人注目的是它简化了添加信息到网页的过程。几乎所有的Web控件都是可读或可写的,你可以读取一样简便地设置文本框控件的Text属性。

举个例子,考虑一下,如果想在网页上更新一段文本来反映用户先前输入的信息,会发生什么呢?在经典的ASP中,你可能需要查找合适的位置来插入脚本块来编写原始的HTML。下面的示例中,用突出的颜色显示了的欢迎信息:

string message = "<span style=\"color:Red\">Welcome " +

FirstName + " " + LastName + "</span>";

Response.Write(message);

另一方面,如果在ASP.NET中定义标签控件,问题就变得更为简单:

<asp:Label id="lblWelcome" runat="server" />

这样,就可以简单地设置它的属性:

lblWelcome.Text = "Welcome " + FirstName + " " + LastName;

lblWelcome.ForeColor = Color.Red;

这种代码主要有这几个优点:首先,编写更容易(不会出现错误编写错误)。在本例中,这点好处看起来微乎其微。但如果需要动态渲染包含了链接、图像和样式的完整的HTML块时,其优点就显出来了。

其次,基于控件的代码更容易放置到网页中。你可以在任何动作产生的地方编写相应的ASP.NET代码。相比而言,在经典的ASP中,你需要考虑内容在页面的什么位置显示,然后合理组织脚本代码块。如果页面有几个动态的区域,脚本块就会出混乱,显示不出清晰的组织和关系。

最后,控件模型精细但同样惊人的优点是它隐藏底层HTML细节的方法。不仅使你不需要学习HTML就能编写代码,而且使你的网页支持多种不同的浏览器。因为控件对自身进行渲染,它能够调整输出来支持广泛的浏览器、增强的客户端特征,甚至是其它HTML相关的标准,如XHTMLWML(用于移动浏览器)。本质上,程序与HTML的关系变得更加松散。

 

ASP.NET事件模型(the ASP.NET Event Model

经典ASP使用线性处理模型。这意味着网页上的代码从开始到结尾按顺序执行的。正因为如此,即便只是一个简单的网页,ASP程序员仍需要编写大量的事件代码。经典的例子是一个网页包含了3个提交按钮,有3个不同的操作。提交网页时,脚本代码需要小心区分点击了哪个按钮,然后正确响应动作。

ASP.NET通过提供新的事件驱动的模型,使事件处理有了全新的改变。在这个模型中,将控件添加到Web窗体,然后确定相应的响应事件。每个事件处理器都封装在一个单独的方法中,这样保持了页面的简洁和组织性。这个模型其实并不新颖,但直到ASP.NET出现之后,它才成为胖客户端应用(rich client application)中Windows UI编程的专门的域。

那么,ASP.NET是如何工作的呢?事实上,这是相当显而易见的。下面是简要的介绍:

1、    网页在第一次运行时,ASP.NET创建页面和控件对象。执行初始化代码,然后页面渲染为HTML并返回给客户端。页面对象在服务器内存中释放。

2、    在某个时刻,用户进行操作,如单击一个按钮,触发回传的操作。此时,窗体数据随着页面提交。

3、    ASP.NET捕获返回的页面,并且重新创建页面对象,并应用视图状态信息。

4、    ASP.NET随后检查触发回送的操作,并唤起对应的事件(如Button.Click),执行事件处理代码。通常情况下,会执行一些服务器端的操作(如更新数据库或从文件读取数据),并且修改控件对象来显示新的信息。

5、    修改后的页面渲染为HTML并返回给客户端。页面对象从内存中释放。如果发生另一次回传,ASP.NET重复第24步的过程。

换句话说, ASP.NET并不直接使用窗体数据来配置控件对象,一般情况下,它使用窗体来确定释放了哪个事件。例如,最后一次回传以后,如果发现文件框中的文本发生改变,ASP.NET就触发一个事件来通知页面,是否对其进行响应取决于你的代码。

请记住,由于HTML是完全无状态的,所有状态需要ASP.NET重新创建才可获得,因此事件驱动模型是竞争的。在这种背景下,ASP.NET在后台执行好几个任务以支持这个模型,这将在后面的小节中看到。这个概念的优美之处在于入门级程序员不需要熟悉系统基础就能使用服务器端的事件。

 

自动回传(Automatic Postbacks

当然,在前面描述的事件系统里有一个缺陷。Windows开发人员早已非常熟悉丰富的事件模型,允许程序响应鼠标移动、按键和精细的控件交互。但在ASP.NET中,客户端事件是发生在客户端的,而服务器进程则位于Web服务器上。这意味着在响应事件时有大量棘手的负载需要处理。正因为如此,在ASP.NET世界里快速释放事件(如鼠标移动)是完全不切实际的。

注意,如果你想完成一定的UI效果,你可能需要使用客户端JAVAScript处理快速事件,如鼠标移动。或者,你可以使用内置了这些事件的自定义的ASP.NET控件。但是,所有的业务代码都必须在安全、功能完备的服务器环境中执行。

如果你熟悉HTML窗体,你会知道这种提交页面的基本方法,即通过点击提交按钮来提交。如果你在使用标准的HTML服务器控件,这仍是你唯一的选择。但是,一旦页面回传,ASP.NET就能够同时释放其它事件(即,指明输入控件中的值已经发生改变的事件)。

显而易见,这点对于创建丰富的Web窗体是很不够的。幸运的是,ASP.NET Web控件使用自动回传(automatic postback)功能来扩展了这个模型。有了这个功能,输入控件可以释放不同的事件,而且服务器端的代码能够立即对其进行响应。例如,当用户点击一个检查框、改变列表中的选择,或者文本框中的文本并且移动到它其区域时,就会触发回传。这些事件虽然不象Windows应用中的事件那样易获得,但它们相对于提交按钮,则是很大的进步。

 

自动回传悄然进行(Automatic Postbacks “Under the Hood”

为了使用自动回传,只需要简单地将Web控件的AutoPostBack属性设置为true(默认值是false,目的是保证无事件响应时优化性能)。这样,ASP.NET使用客户端的JavaScript来桥接客户端和服务器端的程序。

工作过程是这样的:如果你创建了包含一个或多个Web控件的网页,Web控件被设置为使用AutoPostBack(即将其值设置为true),那么,ASP.NET添加名为_doPostBack()JavaScript函数来渲染HTML页。调用时,它触发回传,将带有所有信息的页面传回Web服务器。

ASP.NET也添加了两个隐含的输入区域,用于_doPostBack()函数传递信息到服务器。这个信息包含了触发事件的控件ID和其它相关信息。区域初始为空。如下所示:

<input type="hidden" name="__EVENTTARGET" value="" />

<input type="hidden" name="__EVENTARGUMENT" value="" />

_doPostBack()函数里应当具有设置有关事件的值和提交窗体功能的功能。下面是_doPostBack()的示例:

<script language="javascript">

<!--

function __doPostBack(eventTarget, eventArgument) {

var theform = document.Form1;

theform.__EVENTTARGET.value = eventTarget;

theform.__EVENTARGUMENT.value = eventArgument;

theform.submit();

}

// -->

</script>

记住,_doPostBack()函数由ASP.NET自动产生。当添加更多的AutoPostBack控件到页面时,这段代码会变得更长,因为必须为每个控件设置事件数据。

最后,任何一个将AutoPostBack属性设置为true的控件都会使用onClickonChange属性来连接到_doPostBack()函数。这些属性表明,为了响应客户端的JavaScript事件onClickonChange,浏览器应当执行相应动作。

下面的示例显示了一个用于自动回传的列表控件的标签,标签命名为lstCountry。无论何时用户改变了列表的选择项,就会释放客户端的onChange事件。浏览器于是调用_doPostBack()函数,将页面发送回服务器。

<select id="lstCountry" onchange="__doPostBack('lstBackColor','')"

language="javascript">

换句话说,ASP.NET使用_doPostBack()函数作为中介,自动将客户端的JavaScript事件转换为服务器端的ASP.NET事件。如果你是一位有经验的ASP开发人员,你可能要为传统的ASP网页手动创建类似的解决方法。但在ASP.NET中,自动处理了这些细节,大大节约了时间。

提示,请记住,ASP.NET包含两个控件模型:纯粹的HTML服务器控件和功能更丰富的web控件。自动回传仅web控件中可用。

 

视图状态(View State

ASP.NET模型中,最为重要的要素是新的视图状态机制。视图状态解决了由于HTTP与生俱来的无状态(变化信息丢失)带来的问题。

页面每次回传,你接收所有用户在<form><input>控件中输入的信息。ASP.NET于是以它本来的状态加载网页(基于布局和定义),并且根据这些新信息对页面进行调整。但在动态的Web窗体中,程序改变的内容更多,问题就出现了。例如,你可能编程改变页头的颜色,修改一段静态文本,隐藏或显示控件面板,甚至绑定一个完整的表的数据到Grid。这些动作在它们初始状态时使页面发生变化。然而,这些信息并没有在回传的窗体数据中反映出来。这意味这些信息会在回传后丢失。传统的方法是,使用简单的cookie、基于会话的cookie和各种其它的工作区,可以克服无状态的问题。

为了解决这个限制,ASP.NET设计了综合的状态序列化机制。本质上,一旦页面代码运行完毕(渲染成HTML发送到客户端之前),ASP.NET检查页面上所有控件的所有属性,如果任何一个属性在初始状态过程中发生了改变,ASP.NET将这个信息保存在名字/值集中。最后,ASP.NET将所有收集到的信息序列化为Base64的字符串(Base64串保证了没有无效的HTML特殊字符)。然后插入<form>节中间作为新的隐含字段。

页面回传时,ASP.NET执行如下步骤:

1ASP.NET重新创建基于默认值的页面和控件对象。因此,页面与第一次被请求时具有相同的状态。

2、接下来,反序列化并应用视图状态信息,并且更新所有控件,使页面返回到最后发送给用户前的状态。

3、最后,ASP.NET基于传回的窗体数据调整页面。例如,如果用户在文本框控件中输入了新的文本,或者在列表框里有了新的选择,那些信息存于窗体集中,ASP.NET使用这些信息来修改对应的控件。完成这一步之后,页面反映当前状态,正如呈现给用户一样。

4、现在该轮到事件处理代码工作了。ASP.NET触发相应的事件,响应程序相应地改变页面,跳转到新页面,或者执行一个完全不同的操作。

使用视图状态,是一个了不起的解决方案,因为服务器资源在每次请求之后就释放了,因此保证了可扩展性,使服务器响应成百上千的请求而不会瘫痪。然后,这仍会付出代价,因为视图状态存储于网页之中,导致页面尺寸的增加。这对客户端的影响是双重的,因为客户端不仅需要接受一个更大的页面,而且客户端在下一次回传时,仍需要将隐含的视图状态数据发送回服务器,从而导致接收和发送网页所需时间更长。对于一个简单的页面,负载很小,但如果配置了复杂的、数据量大的控件,如GridView,视图状态信息增长到一定程度会耗尽资源。在这些情况下,可以通过设置EnalbeViewStatent属性为false禁用视图状态。然后,在每个回传里重新初始化控件。

注意,即便将EnableViewState设置为false控件仍能够获取少量的视图状态信息,保证其能正常工作。这种特有的视图状态信息也叫控件状态,它永远也不能被禁用。然而,在设计良好的控件中,控件状态的大小应该非常小。在ASP.NET2.0中,控件状态是一个新的功能,在第27章设计自定义控件时,你将了解到它的工作原理。

ASP.NET仅仅在页面和控件属性中使用视图状态,在成员变量和其它数据中并没有这一过程。但是,在本书的后面,你仍可以将其它的数据类型放入视图状态中,并且随后手工获取这个信息。

3-2提供了端到端的浏览,汇总了页面请求的全部概念。

ASP.NET页面请求

3-2 ASP.NET页面请求

注意,作为一个ASP.NET程序员,记住这一点是很重要的,即Web窗体在每一轮往返过程中都要重新创建。它在内存中保存的时间不会比将其渲染一个请求的时间更长。

 

视图状态悄然进行(View State “Uder the Hood”

如果你仔细查看了ASP.NET渲染的HTML,你可以很容易发现带了视图状态信息的隐藏输入域。下面的示例显示了使用一个标签Web控件的页面,标签设置为动态信息“Hello world”:

<html>

<head runat="server">

<title>Hello World Page</title>>

</head>>

<form name="Form1" method="post" action="WebForm1.aspx" id="Form1">

<div>

<input type="hidden" name="__VIEWSTATE" value="/wEPDwUKLTE2MjY5MTY1

NQ9kFgICAw9kFgICAQ8PFgIeBFRleHQFDEhlbGxvIFdvcmxkIWRkZPsbiNOyNAufEt7OvNIbVYc

GWHqf" />

</div>

<div>

<input type="submit" name="Button1" value="Button" id="Button1" />

<span id="lbl">Hello, world</span>

</div>

</form>

</html>

视图状态串是不可阅读的,它看起来一系列随机的字符。然而,不可忽视的是,用户要描述这个数据却轻而易举。下面的小段.NET代码完成了这个工作,并且将信息解码到网而中。

// viewStateString contains the view state information.

// Convert the Base64 string to an ordinary array of bytes

// representing ASCII characters.

byte[] stringBytes = Convert.FromBase64String(viewStateString);

// Deserialize and display the string.

string decodedViewState = System.Text.Encoding.ASCII.GetString(stringBytes);

lbl.Text = decodedViewState;

在网页中,你可以看到下面这样的内容:

可以看到,控件文本就一目了然了(其它不可打印的字符是空文本框的渲染结果)。这意味着,如果不采取额外的措施,视图状态并不是存储不允许看到的敏感信息的最佳位置。这类敏感数据应当存放在服务器上。另外,基于视图状态,你不能够确定是否客户端篡改了视图状态数据,从而威胁应用的安全。

幸运的是,要提高视图状态的安全性是可能的。你可以启用自动哈希编码来阻止视图状态篡改,甚至可以对视图状态进行加密来防止被解码。这个技术使隐含域从笨拙的工作区提升为更为强健和高质量的架构。在第6章中可以了解到这些技术。

注意,如果你曾经使用ASP.NET1.1编过程,你可能会注意到,在ASP.NET2.0中的视图状态序列化模型与以前版本有所不同。不再采用分号和尖括号来分离值的方式,ASP.NET2.0使用了不可打印字符,使处理字符串更为高效(因为从标记中区分序列化数据更容易)和精简。ASP.NET2.0也为许多普通数据类型减少了序列化的大小,包括布尔值、整型、多次重复的字符串(这很常见,因为不同的控件常常有相同的属性名)。这细微的改变能够产生显著的效果。根据序列化的视图状态的分隔符的数量、使用的数据类型,数据负载重的控件,能够将其视图状态缩小一半甚至更多。

 

视图状态分片(View State Chuncking

隐含的视图状态域并没有大小限制。但是,一些代理服务器和防火墙会禁止隐含域大于某个值的网页通过。为了解决这个问题,可以使用视图状态分片(view state chunking)技术,它能自动将视图状态切分到多个域,保证隐含域的大小不会超过设置的阀值。

为了使用视图状态,你仅需要设置maxPageStateFieldLength属性,这个属性是web.config文件的<pages>的属性。它指定了视图状态的最大大小(单位为字节)。下面的示例将视图状态上限设置为1KB

<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">

<system.web>

<pages maxPageStateFieldLength = "1024" />

</system.web>

</configuration>

当请求的页面所产生的视图状态大于这个值时,就会创建几个隐含的输入域:

<input type="hidden" name="__VIEWSTATEFIELDCOUNT" value="3" />

<input type="hidden" name="__VIEWSTATE" value="..." />

<input type="hidden" name="__VIEWSTATE1" value="..." />

<input type="hidden" name="__VIEWSTATE2" value="..." />

记住,视图状态分片是一个简单的机制,是为了避免特定的代理产生的问题(相对来说发生较少)。视图状态分片不能改善性能(反而增加了少量额外的序列化负载)。在大量良好的设计当中,应该尽可能减少视图状态中的信息,这样可以提升性能。

 

XHTML兼容

ASP.NET1.1中移植过来的大部分ASP.NET 2.0Web控件都是与XHTML1.1标准兼容的。但是,网页其余部分的动作规则取决于你,ASP.NET并没有采取任何措施来强制XHTML与你的网页兼容。

注意,XHTML支持并不向网页中添加任何函数,网页并不一定要使用HTML4.01。但是,由于XHTML是一个严格的标准,它仍有一些好处。例如,你可以确认XHTML网页来捕获在特定浏览器中无法正常工作的细小错误。最为重要的是,XHTML也是有效的XML文档,应用程序对其进行读取和分析更为容易,并且使其具有可扩展性。现在普遍认为XHTML将会替代HTML。了解XHTML的更多内容,参阅规范 http://www.w3.org/TR/xhtml11

除了几个例外情况,所有的ASP.NET服务器控件能够使用XHTML兼容的标签来渲染自身。这意味着标签要遵循XHTML规则,这些规则包含:

l         标签和属性必须是小写。

l         所有元素必须关闭,要么是专门的关闭标签(<p></p>),要么使用空标签进行自关闭(<br/>)。

l         所有的值必须包含在引号之中(如,runat = “server”)。

l         必须使用id属性而不使用名字属性。

XHTML移除了一些在HTML中支持的功能,如框架和不使用CSS的内嵌格式。在多数情况下, XHTML能够满足需要。当然,仍可以使用target属性来创建一个链接,目标页面在新窗口打开。下面的ASP.NET控件可能使用target属性:

• AdRotator

• TreeNode

• HyperLink

• HyperLinkColumn

• BulletedList

使用target属性,在现代的浏览器中不会出现问题。但是,如果你想要创建完全兼容XHTML的站点,应该避免使用这些控件。

需要注意的是,目前使用XHTML不会获得更多的好处。但是,一些公司和组织基于对未来标准的展望,要求使用XHTML。未来,XHMTL将使设计网页变得更为容易,能够适应不同类型的平台,能够被其它应用处理,并且使用新的标签功能使其具有扩展性。例如,你可能使用XSLTXSL转换)、另一种基于XML的标准,将XHTML文档转换为其它文档。而这些功能在HTML网页中无法获得。

 

文档类型定义(Document Type Definitions

每一个XHTML文档都从文档类型定义开始的,文档类型定义定义了页面所使用的XHTML类型。尽管ASP.NET Web控件对XHTML是兼容的,但是ASP.NET并不会自动认为你要创建一个XHTML兼容的网页,因此,它不会自动产生文档类型定义。如果你要创建一个XHTML网页,则需要添加文档类型定义(doctype)。文档类型定义必须放置在网页的标记部分,紧随着页面指令(Page directive)之后。这样,文档类型定义将作为文档的第一行被渲染,而这正是所期望的结果。

下面的示例定义了支持XHTML1.1的网页:

<%@ Page Language="C#" AutoEventWireup="true"

CodeFile="TestPage.aspx.cs" Inherits="TestPage_aspx" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"

"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head runat="server">

<title>Untitled Page</title>

</head>

<body>

<form id="form1" runat="server">

<div>

...

</div>

</form>

</body>

</html>

页面也为<html>元素定义了XML名字空间。这是XHTML所要求的另一细节,但ASP.NET并不会自动提供。

注意,在Visual Studio中创建网页时,为<html>元素定义了XML名字空间,并且为XHTML1.1添加了doctype。你可以修改doctype,甚至移除它。

如果你并不想支持全部的XHTML1.1标准,可以进行一些折衷。通用的方法是使用XHTML1.0 过渡,它增强了XHTML的结构规则,但允许使用被样式表替代和废弃的HTML格式化特征。下面doctype的示例:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

" http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd ">

XHTML 过渡文档类型(doctype)认为HTML中已经被废弃框架。如果需要创建框架页面,考虑使用XHTML1.0 框架集文档类型(frameset doctype):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"

"http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

记住,ASP.NET服务器控件使用任何一种doctype都能正常工作(即便浏览器只支持HTML也是一样)。在网页中采用什么层次的兼容标准完全取决于你。

注意,如果希望采用XHTML标准,可以使用MIME 内容类型application/xhtml+xml替代标准的text/html。这个变化是XHTML推荐的,用来帮助浏览器和其它应用区分传统的HTML网页和XHTML。不幸的是,本书付梓之时,Internet Explorer仍然不支持application/xhtml+xml内容类型(而其它现代的浏览器都提供了支持)。如果仍想实现这种改变,只要添加这个属性到页指令中就可以了: ContentType=“application/xhtml+xml”

 

XHTML确认(XHTML Validation

ASP.NET的核心控件遵循XHTML规则,但要确认网页是XHTML兼容的,需要对添加的任何静态内容进行确认,保证它们也遵循这些规则。Visual Studio内嵌的检查器可以帮助检查。在HTML源编辑工具栏的下拉列表中选择target标准。便如,如果选择了XHTML1.1Visual Studio标记了结构性的错误和废弃的标签(图3-3所示)。

在Visual Studio中检验XHTML1.1

3-3 Visual Studio中校验XHTML1.1

请记住,如果违反了XHTML规则,浏览器不会标记错误。要创建一个XHTML兼容的页面,可以使用Internet Explorer的智能感知,但这必须在网页源文件中进行,同时并不能保证最终的页面没有包含违反XHTML的内容。例如,你可能使用了第三方的控件,而此控件渲染的标签并不是XHTML兼容的。要对网页进行严格测试,需要使用第三方的检查器来请求网页,并且扫描所有错误。

一个很好的免费资源是W3C的校验服务 http://validator.w3.org.。只需要简单地将URL输入网页中,然后点击check。也可以上传一个文件来检查它,但这时必须保证上传的文件是最终的渲染页面,而不是.aspx源码。也可以通过IE查看页面渲染后的内容,点击视图—>查看源文件。

 

禁用XHTML渲染(Disabling XHTML Rendering

如果正在请求的浏览器支持HTML4.0或者更高版本,ASP.NET服务器控件会自动使用XHTML标记。但是,可能有一种不常见的情况,即你希望禁用XHTML兼容渲染。当客户端的JavaScript依赖于一些在XHTML中不允许的标签时,这种情况就会出现。为了解决这个问题,可以返回去使用ASP.NET1.1中的HTML渲染。

为了返回去使用纯HTML渲染,只需要将Web.config文件中xhtmlConformance元素的mode属性设置为legacy。另两种可选择的方案是transitional(默认)strict。选择最适合你的doctype的选项。下面是一个示例:

<system.web>

<xhtmlConformance mode="legacy" />

</system.web>

启用了过期的(obsolete)渲染后,ASP.NET控件不再使用任何XHTML优化,而这些优化在HTML4.01中并不要求严格兼容。例如,它们渲染了标准的HTML元素,如<br>,而不是正确的XHTML版本<br/>。但是,即便启用了过期的渲染,ASP.NET仍不会自动清除<html>标签中的名字空间,也不会移除doctype(如果已经存在于网页之中)。

注意,ASP.NET不保证非XHTML渲染将会在ASP.NET的未来版本中得到支持,所以对其的使用应该仅限于特殊场合。

 

Web窗体处理过程(Web Forms Processing Stages

在服务器端,对于ASP.NET Web窗体的处理是分阶段进行的。在每一个阶段,不同的事件被唤起。这允许在任何一阶段,向网页中插入处理流,并且进行你需要的响应。

下面列出了ASP.NET网页处理流中的主要阶段:

l         页面框架初始化

l         用户代码初始化

l         校验

l         事件处理

l         自动进行数据绑定

l         清除

请记住,对于每一次Web请求,这些阶段是独立进行的。图3-4显示了这些阶段的顺序。存在的阶段远不止列出这些,但是列出这些是在编写自己的ASP.NET控件时经常用到的,而且它们不会由页面直接处理。

ASP.NET页生命周期

3-4 ASP.NET页生命周期

后面的小节中,你将深入了解每个阶段,并且会对一个简单的Web页面示例进行测试。

 

页面框架初始化(Page Framework Initialization

这是ASP.NET创建网页的第一个阶段。这一阶段产生.aspx网页中标签定义的所有控件。如果页面不是第一次请求(也就是说是回传的页面),ASP.NET将反序列化视图状态信息并应用到所有控件。

在这一阶段,网页的Page.Init事件释放。但是,这个事件很少由网页进行处理,因为这一阶段尚未创建控件对象,也没有加载视图状态信息,执行页面初始化太早了点。

 

用户代码初始化

在这个处理阶段,Page.Load事件被释放。大多数网页处理这个事件,执行任何要求的初始化(如添充动态文本或配置控件)。

无论页面是首次被请求还是回传的请求的一部分,Page.Load事件总是会释放。幸运的是,ASP.NET提供了允许程序员区分首次加载和后续加载的方法。这为什么很重要?首先,由于视图状态是自动维护的,你必须在第一次页面加载时从动态数据源获取数据。而在回传页面中,除了根据根据视图状态信息存储控件属性外,不需要做任何其它事情。如果重新创建信息(例如,从数据库查询数据)的代价很大,这样做就可以大幅提升性能。其次,也有其它的场合,如编辑窗体和drill-down页面,需要在首次加载和后续加载的页面中显示不同的接口,这就需要对首次还是后续加载进行区分。

为了确定页面的当前状态,你可以检查静态的Page.IsPostBack属性,这个属性在网页第一次请求中被设置为false。示例如下:

if (!Page.IsPostBack)

{

// It's safe to initialize the controls for the first time.

FirstName.Text = "Enter your name here";

}

注意,IsPostBackPage类的静态属性。它总是基于当前的页面返回信息。也可以使用实例属性IsPostBack(如this.IsPostBack),返回相同的值,这种方法往往更受人偏爱。

记住,视图状态存储每个已变化的属性。Page.Load事件中对控件进行初始化也算作改变,因此接触到的任何控件值都将保存在视图状态中,这不必要地增大了网页的大小并且延长了传输的时间。为简化视图状态,并且保持页面尺寸较小,应避免在代码中对控件进行初始化设置。相反,在控件标签中设置属性(在源文件状态手工编辑标签或者使用属性窗口)。这样,这些细节不会保存在视图状态中。在代码中使初始化控件更为方便的时候,可以通过设置EnableViewStatefalse来禁用控件的视图状态,并且在Page.Load事件释放时初始化控件,无论当前的请求是不是回传的。

 

校验(Validation

ASP.NET引入了新的校验控件,能够自动校验其它用户输入控件,并且显示出错信息。这些控件在页面加载后其它事件发生前释放。但是,校验控件主要为那些大部分自供给(这意味着你不需要对校验事件进行响应)的控件服务。你可以检查页面在其它事件句柄里是否有效(使用Page.IsValid属性)。第4章详细讨论了校验器控件。

 

事件处理(Event Handling

现在,页面完全加载并且进行了校验。ASP.NET释放所有从最后一次回传以后发生的事件。大部分ASP.NET事件是下面两种类型之一:

l         立即响应事件(Immediate response events):包括单击提交按钮,或者点击在一个丰富的Web控件中的其它按钮、图像区域或者链接,通过调用_doPostBack() JavaScript函数触发回传。

l         变化事件(Change events):包括控件中的选择或者文本框中的文本发生的变化。如果AutoPostBack设置为true,这些事件就为Web控件立即释放。否则,在页面下次回传时释放事件。

你已经看到,ASP.NET事件模型与传统的Windows环境大为不同。在Windows应用中,窗体状态存放在内存中,程序连续不断地运行。这就是意味着你可以立即对事件进行响应。在ASP.NET中,所有的事件都分阶段发生,因此事件有时分批批处理的。

例如,假设有一个页面,页面上有一个提交按钮和一个文本框,它们并不自动回传。你改变文本框中的文本,然后点击提交按钮。此时,ASP.NET顺序唤起下列事件:

• Page.Init

• Page.Load

• TextBox.TextChanged

• Button.Click

• Page.PreRender

• Page.Unload

从这一点信息,本质上可以帮助你开发ASP.NET更为容易。对事件驱动模型,它有上层和下层。上层就是事件模型提供了更高层的抽象,它使你的代码保持清晰,方便维护状态。下层就是不需要考虑事件模型本身的竞争。这会使你设想并没有保持true(如期望将信息保存在成员变量中),或者设计结果不会很好执行(如在视图状态中存储大量信息)。(这一段翻译可能有问题

 

自动数据绑定(Automatic Data Binding