本章内容
q 验证控件概述
q 使用RequiredFieldValidator控件
q 使用RangeValidator控件
q 使用CompareValidator控件
q 使用RegularExpressionValidator控件
q 使用CustomValidator控件
q 使用ValidationSummary控件
q 创建自定义验证控件
q 小结
本章学习当表单提交到Web服务器端时,如何验证表单字段。可以使用验证控件防止用户向数据库表提交错误类型的数据。比如,可以使用验证控件来预防用户给生日数据字段提交值“Apple”。
本章的第一部分概览ASP.NET 2.0 Framework包含的标准验证控件;学习如何控制验证错误的显示,如何突出显示验证错误信息,如何使用验证控件组;提供所有标准验证控件使用的示例代码。
接下来,我们通过自定义验证控件扩展基本验证控件。比如,学习如何创建可以在客户端调用服务器端验证函数的AjaxValidator控件。
3.1 验证控件概述
ASP.NET 2.0 Framework包含6个验证控件:
q RequiredFieldValidator——用于要求用户在表单字段中输入必需的值。
q RangeValidator——用于检测一个值是否在确定的最小值和最大值之间。
q CompareValidator——用于比较一个值和别一个值或执行数据类型检查。
q RegularExpressionValidator——用于比较一个值和正则表达式。
q CustomValidator——用于执行自定义验证。
q ValidationSummary——用于在页面中显示所有验证错误的摘要。
验证控件可以关联任何ASP.NET Framework中的表单控件。比如,希望要求用户向TextBox控件输入一个值,可以将一个RequiredFieldValidator控件关联到这个TextBox控件。
注解 从技术上讲,可以对任何用ValidationProperty属性的控件使用验证控件。
代码清单3-1中的页面包含一个简单的订单项表单,它包含3个TextBox控件,分别用于输入产品名、产品价格和产品数量。每一个表单字段都使用验证控件进行验证。
代码清单3-1 OrderForm.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void btnSubmit_Click(Object sender, EventArgs e)
{
if (Page.IsValid)
{
lblResult.Text = @"<br />Product: " + txtProductName.Text
+ "<br />Price: " + txtProductPrice.Text
+ "<br />Quantity: " + txtProductQuantity.Text;
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Order Form</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<fieldset>
<legend>Product Order Form</legend>
<asp:Label
id="lblProductName"
Text="Product Name:"
AssociatedControlID="txtProductName"
Runat="server" />
<br />
<asp:TextBox
id="txtProductName"
Runat="server" />
<asp:RequiredFieldValidator
id="reqProductName"
ControlToValidate="txtProductName"
Text="(Required)"
Runat="server" />
<br /><br />
<asp:Label
id="lblProductPrice"
Text="Product Price:"
AssociatedControlID="txtProductPrice"
Runat="server" />
<br />
<asp:TextBox
id="txtProductPrice"
Columns="5"
Runat="server" />
<asp:RequiredFieldValidator
id="reqProductPrice"
ControlToValidate="txtProductPrice"
Text="(Required)"
Display="Dynamic"
Runat="server" />
<asp:CompareValidator
id="cmpProductPrice"
ControlToValidate="txtProductPrice"
Text="(Invalid Price)"
Operator="DataTypeCheck"
Type="Currency"
Runat="server" />
<br /><br />
<asp:Label
id="lblProductQuantity"
Text="Product Quantity:"
AssociatedControlID="txtProductQuantity"
Runat="server" />
<br />
<asp:TextBox
id="txtProductQuantity"
Columns="5"
Runat="server" />
<asp:RequiredFieldValidator
id="reqProductQuantity"
ControlToValidate="txtProductQuantity"
Text="(Required)"
Display="Dynamic"
Runat="server" />
<asp:CompareValidator
id="CompareValidator1"
ControlToValidate="txtProductQuantity"
Text="(Invalid Quantity)"
Operator="DataTypeCheck"
Type="Integer"
Runat="server" />
<br /><br />
<asp:Button
id="btnSubmit"
Text="Submit Product Order"
OnClick="btnSubmit_Click"
Runat="server" />
</fieldset>
<asp:Label
id="lblResult"
Runat="server" />
</div>
</form>
</body>
</html>
3个表单字段都单独关联一个RequiredFieldValidator控件。如果试图不给某个字段输入值就提交代码清单3-1中的表单,就会显示一个验证错误信息(见图3-1)。
每个RequiredFieldValidator控件都通过它的ControlToValidate属性来关联某个控件。这个属性接受页面中被验证的控件的名字。

图3-1 显示验证错误信息
CompareValidator控件关联TextBox控件txtProductPrice和txtProductQuantity。第一个CompareValidator控件用于检测txtProductPrice文本字段是否包含货币值,第二个CompareValidator控件用于检测txtProductQuantity文本字段是否包含整数值。
注意将多于一个的验证控件关联到同一个表单字段不会产生任何错误。如果要使一个表单字段必须输入值并检查输入表单字段的数据类型,那么就需要给表单字段同时关联RequiredFieldValidator控件和CompareValidator控件。
最后,注意btnSubmit_Click()处理程序在表单数据提交后检查了Page.IsValid属性。当使用验证控件时,在对提交到页面的数据做任何事情之前,都应检查Page.IsValid属性。当该属性返回True时,且只有该属性返回True时,页面上才不显示验证错误。
3.1.1 验证控件与JavaScript
验证控件会在客户端(浏览器)和服务器端都默认执行验证。验证控件使用客户端JavaScript。从用户体验的角度来看,这非常棒,因为无论何时把一个无效的值输入表单字段都能立即得到反馈。
注解 RequiredFieldValidator在你至少尝试了一次提交表单或在表单字段中输入、移除数据后才执行客户端验证。
所有高端浏览器都支持这些客户端JavaScript,支持的浏览器包括IE、FireFox和Opera。相对于只支持IE这一种高端浏览器的旧版本ASP.NET来说,这是一个变化。
可以在不支持(或禁用了)JavaScript的浏览器中使用验证控件。如果浏览器不支持JavaScript,那么在显示验证错误信息之前,表单必须先要回传到服务器端。
就算在客户端进行了验证,在服务器端仍然要执行验证。这样做是出于安全考虑,即便有人伪造了表单,并把表单数据提交到Web服务器端,他仍然不能提交无效的数据。
如果你愿意,可以把任何验证控件的EnableClientScript属性设为False来禁用它的客户端验证。
3.1.2 使用Page.IsValid
如前所述,处理含有验证控件的表单数据提交,应当总是检查Page.IsValid属性。每一个验证控件都包含一个IsValid属性,如果没有验证错误,这个属性返回True。如果页面中所有验证控件的IsValid属性都返回True,那么Page.IsValid属性返回True。
很容易忘记检查Page.IsValid属性。如果使用支持验证控件的JavaScript的高端浏览器,当有验证错误时,它会防止你把表单提交到服务器端。但是,假如有用户使用的浏览器不支持JavaScript,那么就算有验证错误,页面也会被提交到服务器端。
例如,使用不支持JavaScript的浏览器请求代码清单3-1中的页面,并且在不输入表单数据的情况下就提交表单,此时,btnSubmit_Click()事件处理程序就会在服务器端执行。代码清单3-1使用Page.IsValid属性防止低端浏览器显示无效的表单数据。
注意 很不幸,在创建应用程序时,我多次犯下错误,忘记包含Page.IsValid检测。因为你通常不会使有低端的浏览器来开发Web应用程序,所以在开始从数据库表中得到无效数据之前,不会注意到本节描述的这个问题。
3.1.3 设置Display属性
所有的验证控件都含有Display属性,用来决定如何呈现验证错误信息。该属性接受以下3个可能值:
q Static
q Dynamic
q None
默认情况下,Display的值是Static。当Display属性为这个值时,验证控件生成的验证错误信息如下:
<span id="reqProductName" style="color:Red;visibility:hidden;">(Required)</span>
注意,错误信息呈现在一个<span>标签中,这个标签包含一个CSS属性,设置<span>标签的visibility为hidden。
另一方面,如果Display属性设置为Dynamic,错误信息则如下:
<span id="reqProductName" style="color:Red;display:none;">(Required)</span>
在这个例子中,CSS的display属性隐藏了<span>标签的内容。
visibility和display属性都能用于在浏览器中隐藏文本。但是,用visibility属性隐藏文本仍然会占用屏幕空间。用display属性隐藏文本,则不会占用屏幕空间。
通常情况下,应当将验证控件的Display属性设置为Dynamic。这样,显示在验证控件后面的控件的内容就不会被推到右边。所有流行的浏览器(IE、FireFox和Opera)都支持CSS的display属性。
第三个Display属性可能的值是None。如果愿意,可以不用单个的验证控件来显示错误信息,而由ValidationSummary控件来显示错误信息。本章的后面部分将会学到如何使用ValidationSummary控件。
3.1.4 突出显示验证错误
验证控件显示验证错误时,它显示的是其Text属性的值。我们通常会给Text属性赋一个简单的文本字符串,比如“(必填)”。其实Text属性接受任何HTML字符串。
比如,当没有为First Name文本字段输入值就提交表单时,代码清单3-2中的页面会显示一个图片(见图3-2)。
代码清单3-2 ValidationImage.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Validation Image</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label
id="lblFirstName"
Text="First Name"
AssociatedControlID="txtFirstName"
Runat="server" />
<br />
<asp:TextBox
id="txtFirstName"
Runat="server" />
<asp:RequiredFieldValidator
id="reqFirstName"
ControlToValidate="txtFirstName"
Text="<img src='Error.gif' alt='First name is required.' />"
Runat="server" />
<br /><br />
<asp:Button
id="btnSubmit"
Text="Submit"
Runat="server" />
</div>
</form>
</body>
</html>

图3-2 为验证错误显示一个图片
在代码清单3-2中,Text属性包含一个HTML <img>标签。当存在验证错误时,该<img>标签表示的图片就会显示出来。
另一种强调错误的方式是使用SetFocusOnError属性,所有的验证控件都支持这个属性。如果这个属性的值为True,当存在验证错误时,表单焦点就会自动地转移到验证控件所关联的控件上。
比如,代码清单3-3中的页面包含两个TextBox控件,这两个控件都由RequiredFieldValidator控件来验证。每个RequiredFieldValidator控件的SetFocusOnError属性都被启用。如果仅为第一个文本字段提供值而不为第二个文本字段提供值就提交表单,表单焦点就会自动转移到第二个表单字段。
代码清单3-3 ShowSetFocusOnError.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Show SetFocusOnError</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label
id="lblFirstName"
Text="First Name"
AssociatedControlID="txtFirstName"
Runat="server" />
<br />
<asp:TextBox
id="txtFirstName"
Runat="server" />
<asp:RequiredFieldValidator
id="reqFirstName"
ControlToValidate="txtFirstName"
Text="(Required)"
SetFocusOnError="true"
Runat="server" />
<br /><br />
<asp:Label
id="lblLastName"
Text="Last Name"
AssociatedControlID="txtLastName"
Runat="server" />
<br />
<asp:TextBox
id="txtLastname"
Runat="server" />
<asp:RequiredFieldValidator
id="reqLastName"
ControlToValidate="txtLastName"
Text="(Required)"
SetFocusOnError="true"
Runat="server" />
<br /><br />
<asp:Button
id="btnSubmit"
Text="Submit"
Runat="server" />
</div>
</form>
</body>
</html>
最后,如果想真正地强调关联验证错误的控件,那么可以利用Page.Validators属性。该属性公开页面的验证控件集合。在代码清单3-4中,利用Page.Validators属性突出显示每一个有验证错误的控件(见图3-3)。

图3-3 改变表单字段的背景色
代码清单3-4 ShowValidators.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void Page_PreRender()
{
foreach (BaseValidator valControl in Page.Validators)
{
WebControl assControl = (WebControl)Page.FindControl(valControl.ControlToValidate);
if (!valControl.IsValid)
assControl.BackColor = System.Drawing.Color.Yellow;
else
assControl.BackColor = System.Drawing.Color.White;
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Show Validators</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label
id="lblFirstName"
Text="First Name"
AssociatedControlID="txtFirstName"
Runat="server" />
<br />
<asp:TextBox
id="txtFirstName"
Runat="server" />
<asp:RequiredFieldValidator
id="reqFirstName"
ControlToValidate="txtFirstName"
Text="(Required)"
EnableClientScript="false"
Runat="server" />
<br /><br />
<asp:Label
id="lblLastName"
Text="Last Name"
AssociatedControlID="txtLastName"
Runat="server" />
<br />
<asp:TextBox
id="txtLastname"
Runat="server" />
<asp:RequiredFieldValidator
id="reqLastName"
ControlToValidate="txtLastName"
Text="(Required)"
EnableClientScript="false"
Runat="server" />
<br /><br />
<asp:Button
id="btnSubmit"
Text="Submit"
Runat="server" />
</div>
</form>
</body>
</html>
Page.Validators属性用在Page_PreRender()处理程序中。Page.Validators集合中的每个控件的IsValid属性都被检查。如果IsValid返回False,那么由该验证控件验证的控件用黄色背景突出显示。
3.1.5 使用验证组
在ASP.NET Framework的第一个版本中,没有简易的方式给同一个页面加入两个表单。如果在一个页面加入多于一个的表单,并且每个表单都包含验证控件,那么提交表单时所有表单中的验证控件都会进行评估,而不管提交的是哪个表单。
比如,假设想要创建一个同时包含登录和注册表单的页面,登录表单放在左栏,注册表单放在右栏,两个表单都包含验证控件,那么提交登录表单会评估包括注册表单中验证控件在内的所有验证控件。
在ASP.NET 2.0中,则不必再面对这种局限性了。ASP.NET 2.0 Framework引入验证组(validation group)的概念。验证组用于把相关联的表单字段组合在一起。
例如,代码清单3-5中的页面同时包含登录和注册表单,并且每个表单都有互不相干的一套验证控件。
代码清单3-5 ShowValidationGroups.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void btnLogin_Click(Object sender, EventArgs e)
{
if (Page.IsValid)
lblLoginResult.Text = "Log in successful!";
}
void btnRegister_Click(Object sender, EventArgs e)
{
if (Page.IsValid)
lblRegisterResult.Text = "Registration successful!";
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<style type="text/css">
html
{
background-color:silver;
}
.column
{
float:left;
width:300px;
margin-left:10px;
background-color:white;
border:solid 1px black;
padding:10px;
}
</style>
<title>Show Validation Groups</title>
</head>
<body>
<form id="form1" runat="server">
<div class="column">
<fieldset>
<legend>Login</legend>
<p>
Please log in to our Website.
</p>
<asp:Label
id="lblUserName"
Text="User Name:"
AssociatedControlID="txtUserName"
Runat="server" />
<br />
<asp:TextBox
id="txtUserName"
Runat="server" />
<asp:RequiredFieldValidator
id="reqUserName"
ControlToValidate="txtUserName"
Text="(Required)"
ValidationGroup="LoginGroup"
Runat="server" />
<br /><br />
<asp:Label
id="lblPassword"
Text="Password:"
AssociatedControlID="txtPassword"
Runat="server" />
<br />
<asp:TextBox
id="txtPassword"
TextMode="Password"
Runat="server" />
<asp:RequiredFieldValidator
id="reqPassword"
ControlToValidate="txtPassword"
Text="(Required)"
ValidationGroup="LoginGroup"
Runat="server" />
<br /><br />
<asp:Button
id="btnLogin"
Text="Login"
ValidationGroup="LoginGroup"
Runat="server" OnClick="btnLogin_Click" />
</fieldset>
<asp:Label
id="lblLoginResult"
Runat="server" />
</div>
<div class="column">
<fieldset>
<legend>Register</legend>
<p>
If you do not have a User Name, please
register at our Website.
</p>
<asp:Label
id="lblFirstName"
Text="First Name:"
AssociatedControlID="txtFirstName"
Runat="server" />
<br />
<asp:TextBox
id="txtFirstName"
Runat="server" />
<asp:RequiredFieldValidator
id="reqFirstName"
ControlToValidate="txtFirstName"
Text="(Required)"
ValidationGroup="RegisterGroup"
Runat="server" />
<br /><br />
<asp:Label
id="lblLastName"
Text="Last Name:"
AssociatedControlID="txtLastName"
Runat="server" />
<br />
<asp:TextBox
id="txtLastName"
Runat="server" />
<asp:RequiredFieldValidator
id="reqLastName"
ControlToValidate="txtLastName"
Text="(Required)"
ValidationGroup="RegisterGroup"
Runat="server" />
<br /><br />
<asp:Button
id="btnRegister"
Text="Register"
ValidationGroup="RegisterGroup"
Runat="server" OnClick="btnRegister_Click" />
</fieldset>
<asp:Label
id="lblRegisterResult"
Runat="server" />
</div>
</form>
</body>
</html>
注意验证控件和按钮控件都包含ValidationGroup属性。与登录表单有关的所有控件的ValidationGroup属性都赋值为"LoginGroup"。与注册表单有关的所有控件的ValidationGroup属性都赋值为"RegisterGroup"。
因为把表单字段组合到了不同的验证组,所以可以互不干涉地提交两个表单。提交登录表单不再触发注册表单的验证控件了(见图3-4)。

图3-4 使用验证组
可以给ValidationGroup属性赋任意字符串值。该字符串唯一的目的就是把表单中不同的控件关联到一起并划分到不同的组。
注解 使用验证组对于Web部件非常重要,因为不同表单的多个Web部件可能会加入到同一页面中。
3.1.6 禁用验证
所有的按钮控件——Button控件、LinkButton控件和ImageButton控件——都有CausesValidation属性。如果赋值False给该属性,那么点击这个按钮就会绕过页面中所有的验证。
绕过验证对创建取消按钮很有用。例如,代码清单3-6中的页面有一个取消按钮,用于把用户重定向回Default.aspx页面。
代码清单3-6 ShowDisableValidation.aspx
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
void btnCancel_Click(Object sender, EventArgs e)
{
Response.Redirect("~/Default.aspx");
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Show Disable Validation</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Label
id="lblFirstName"
Text="First Name:"
AssociatedControlID="txtFirstName"
Runat="server" />
<asp:TextBox
id="txtFirstName"
Runat="server" />
<asp:RequiredFieldValidator
id="reqFirstName"
ControlToValidate="txtFirstName"
Text="(Required)"
Runat="server" />
<br /><br />
<asp:Button
id="btnSubmit"
Text="Submit"
Runat="server" />
<asp:Button
id="btnCancel"
Text="Cancel"
OnClick="btnCancel_Click"
CausesValidation="false"
Runat="server" />
</div>
</form>
</body>
</html>
注意,代码清单3-6中的取消按钮带有一个值为False的CausesValidation属性。假如该按钮不带有这个属性,那么当点击取消按钮时RequiredFieldValidator控件会阻止你提交表单。






