一 、實現(xiàn)的效果
我想對于ASP.NET的Validator控件已經(jīng)熟悉的不能再熟悉了。我們 已經(jīng)習(xí)慣了用Validator控件來驗證我們在表單的輸入,并通過ValidationSummary來輸出我們?yōu)閂alidator控件設(shè)置的Error message。不知道大家有沒想過進一步改進一下我們的Validation來改善我們的User Experience。比如,在ValidationSummary輸出一個Link連接到對應(yīng)的控件,而不是顯示單純的Error message。

比如在上圖中,是一個典型的Login的Page。我們有兩個必填的字段:User name和Password。為此我定義兩個RequiredFieldValidator。他們的Error message分別為:”User name is mandatory!”和”Password is mandatory!”。在未輸入任何值得前提下Click “Sign in”按鈕,Error Message被顯示在ValidationSummary上面。不過和傳統(tǒng)的Error message不同,顯示在ValidationSummary上的實際上是兩個鏈接,Click對應(yīng)的Error message,光標會設(shè)置到對應(yīng)的Textbox上。比如上圖所示:Click ”User name is mandatory!”,光標回到User name對應(yīng)的Texbox。
二、具體實現(xiàn)
現(xiàn)在我們來簡單敘述上面的效果是如果實現(xiàn)的,在開始之前我想說的是,方法非常簡單—或許你已經(jīng)猜到了:)
1.首先來看看aspx。
<%
@?Page?Language="C#"?AutoEventWireup="true"?CodeFile="Login.aspx.cs"?Inherits="Login"? %>
<! 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 > Login </ title >

???? < style? type ="text/css" >

????????body{
}{font-family:Verdana;?font-size:10px}

????????table{
}{width:300px}

????????table?tr{
}{height:30px}

????????table?td.firstColumn{
}{width:100px;?text-align:right}

????????table?td.secondColumn{
}{?text-align:left}

????????table?span.asterisk{
}{color:red}

????????table?.textbox{
}{width:150px;?border:solid?1px?#999999}

????????table?.button{
}{background-color:?#00cc66;border:solid?1px?#999999}

????????ul?li{
}{margin-bottom:5px}?

????????ul?li?a{
}{color:red;?text-decoration:none}

????????ul?li?a:hover{
}{text-decoration:underline}?
??? </ style >
???

??? < script? type ="text/javascript" >
????????function?setFocus(control)

???????
{
???????????var?controlToValidate?=?document.getElementById(control);
???????????controlToValidate.focus();
???????}?
???????
??? </ script >
</ head >
< body? style ="font-family:?Verdana" >
???? < form? id ="form1" ?runat ="server" >
???????? < div >
???????????? < table? cellpadding ="0" ?cellspacing ="5px" >
???????????????? < tr >
???????????????????? < td? colspan ="2" >
???????????????????????? < asp:ValidationSummary? runat ="server" ?ID ="vldLogin" ? />
???????????????????? </ td >
???????????????? </ tr >
???????????????? < tr >
???????????????????? < td? class ="firstColumn" >
????????????????????????User?Name:? < span? class ="asterisk" > * </ span ></ td >
???????????????????? < td? class ="secondColumn" >
???????????????????????? < asp:TextBox? runat ="server" ?ID ="txtUserName" ?CssClass ="textbox" ></ asp:TextBox >
???????????????????????? < asp:RequiredFieldValidator? runat ="server" ?ID ="rqfUserName" ?ControlToValidate ="txtUserName" ?Display ="None" ></ asp:RequiredFieldValidator >
???????????????????????? < asp:CustomValidator? runat ="server" ?ID ="ctmUserName" ??Display ="None" ?OnServerValidate ="ctmUserName_ServerValidate" ?ControlToValidate ="txtUserName" ? ></ asp:CustomValidator >
???????????????????? </ td >
???????????????? </ tr >
???????????????? < tr >
???????????????????? < td? class ="firstColumn" >
????????????????????????Password:? < span? class ="asterisk" > * </ span ></ td >
???????????????????? < td? class ="secondColumn" >
???????????????????????? < asp:TextBox? runat ="server" ?ID ="txtPassword" ?TextMode ="Password" ?CssClass ="textbox" ></ asp:TextBox >
???????????????????????? < asp:RequiredFieldValidator? runat ="server" ?ID ="rqfPassword" ?ControlToValidate ="txtPassword" ?Display ="None" ? ></ asp:RequiredFieldValidator >
???????????????????? </ td >
???????????????? </ tr >
???????????????? < tr >
???????????????????? < td? colspan ="2" ?align ="center" >
???????????????????????? < asp:Button? runat ="server" ?ID ="btnSignIn" ?Text ="Sign?in" ?CssClass ="button" ? />
???????????????????????? < asp:Button? runat ="server" ?ID ="ButtonCancel" ?Text ="Cancel" ?CausesValidation ="false"
????????????????????????????CssClass ="button" ? />
???????????????????? </ td >
???????????????? </ tr >
???????????? </ table >
???????? </ div >
???? </ form >
</ body >
</ html >
在看到了上面的Screen shot之后再看看上面的Html,結(jié)構(gòu)清晰得一目了然。所以我就不再進一步解釋了。在這里我只需要提提定義在aspx的一段javascript function:setFocus。通過它把focus設(shè)置到指定的控件。
??? < script? type ="text/javascript" >
????????function?setFocus(control)

???????
{
???????????var?controlToValidate?=?document.getElementById(control);
???????????controlToValidate.focus();
???????}???????
??? </ script >
2.接著我們來看看code behind。
using?System;
using?System.Data;
using?System.Configuration;
using?System.Collections;
using?System.Web;
using?System.Web.Security;
using?System.Web.UI;
using?System.Web.UI.WebControls;
using?System.Web.UI.WebControls.WebParts;
using?System.Web.UI.HtmlControls;

public?partial?class?Login?:?System.Web.UI.Page


{
????protected?void?Page_Load(object?sender,?EventArgs?e)

????
{
????????if?(this.IsPostBack)

????????
{
????????????return;
????????}

????????this.rqfUserName.ErrorMessage?=?string.Format("{0}?is?mandatory!",?"User?name");
????????this.rqfPassword.ErrorMessage?=?string.Format("{0}?is?mandatory!",?"Password");
????????this.ctmUserName.ErrorMessage?=?"Such?a?user?has?not?registered!";

????????this.MakeClickableErrorMessage();
????}

????private?void?MakeClickableErrorMessage()

????
{
????????foreach?(BaseValidator?validator?in?this.Validators)

????????
{
????????????if?(validator.ControlToValidate?==?string.Empty)

????????????
{
????????????????continue;
????????????}
????????????string?clientID?=?this.FindControl(validator.ControlToValidate).ClientID;
????????????string?script?=?string.Format("<a?href=?/"javascript:setFocus('{0}');/">{1}</a>",?clientID,?validator.ErrorMessage);
????????????validator.ErrorMessage?=?script;
????????}
????}

????protected?void?ctmUserName_ServerValidate(object?source,?ServerValidateEventArgs?args)

????
{
????????if?(this.txtUserName.Text.Trim()?!=?"adm")

????????
{
????????????args.IsValid?=?false;
????????????return;
????????}

????????args.IsValid?=?true;
????}
}
Code也簡單得一塌糊涂,除了MakeClickableErrorMessage這個Method,其他的都不值一提。
private ? void ?MakeClickableErrorMessage()

????
{
????????foreach?(BaseValidator?validator?in?this.Validators)

????????
{
????????????if?(validator.ControlToValidate?==?string.Empty)

????????????
{
????????????????continue;
????????????}
????????????string?clientID?=?this.FindControl(validator.ControlToValidate).ClientID;
????????????string?script?=?string.Format("<a?href=?/"javascript:setFocus('{0}');/">{1}</a>",?clientID,?validator.ErrorMessage);
????????????validator.ErrorMessage?=?script;
????????}
????}
顯示在ValidationSummary中原本簡單的literal error message就是通過上面的這個MakeClickableErrorMessage轉(zhuǎn)變成hyperlink的。在上面的code中,我遍歷page中的每個Validator control。如果該Validator control有對應(yīng)ControlToValidate(對于一個Validator control來說,ControlToValidate并非一個必需的property,如果沒有指定該property,其值為空字符串),直接進入下一個循環(huán)。然后我把原來只是彈出的文本轉(zhuǎn)變成一個<a></a>,然后再將其重新賦值給對應(yīng)的Validator contorl的ErrorMessage property。
比如對于rqfUserName?RequiredFieldValidator來說,原來的Error message是”User name is mandatory!”,那么現(xiàn)在的Error message變成了:
< a? href =”javascript:? setFocus(‘txtUserName’);” > ?User?name?is?mandatory! </ a >
三、ASP.NET是如何實現(xiàn)Validation的
上面只是一個簡單的小竅門,我們以這個Sample為例,來進一步介紹ASP.NET如何盡心Validation的。為了簡單起見,在這里我沒法討論所有的Validator control。只介紹RequiredFieldValidator和CustomValidator這兩種Validator control的處理流程。
1.Client side Validation
我們通過IE來瀏覽上面的Page,通過參看Source code,可以看到最后Render出來的html:
<!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"><title>
????Login
</title>

????<style?type="text/css">

????????body{
}{font-family:Verdana;?font-size:10px}

????????table{
}{width:300px}

????????table?tr{
}{height:30px}

????????table?td.firstColumn{
}{width:100px;?text-align:right}

????????table?td.secondColumn{
}{?text-align:left}

????????table?span.asterisk{
}{color:red}

????????table?.textbox{
}{width:150px;?border:solid?1px?#999999}

????????table?.button{
}{background-color:?#00cc66;border:solid?1px?#999999}

????????ul?li{
}{margin-bottom:5px}?

????????ul?li?a{
}{color:red;?text-decoration:none}

????????ul?li?a:hover{
}{text-decoration:underline}?
???</style>???

???<script?type="text/javascript">
????????function?setFocus(control)

???????
{
???????????var?controlToValidate?=?document.getElementById(control);
???????????controlToValidate.focus();
???????}???????
???</script>
</head>
<body?style="font-family:?Verdana">
????<form?name="form1"?method="post"?action="Login.aspx"?onsubmit="javascript:return?WebForm_OnSubmit();"?id="form1">
<div>
<input?type="hidden"?name="__EVENTTARGET"?id="__EVENTTARGET"?value=""?/>
<input?type="hidden"?name="__EVENTARGUMENT"?id="__EVENTARGUMENT"?value=""?/>
<input?type="hidden"?name="__VIEWSTATE"?id="__VIEWSTATE"?value="/wEPDwUKMTg3OTM1NTM2MA9kFgICAw9kFgYCBQ8PFgIeDEVycm9yTWVzc2FnZQVKPGEgaHJlZj0gImphdmFzY3JpcHQ6c2V0Rm9jdXMoJ3R4dFVzZXJOYW1lJyk7Ij5Vc2VyIG5hbWUgaXMgbWFuZGF0b3J5ITwvYT5kZAIHDw8WAh8ABVI8YSBocmVmPSAiamF2YXNjcmlwdDpzZXRGb2N1cygndHh0VXNlck5hbWUnKTsiPlN1Y2ggYSB1c2VyIGhhcyBub3QgcmVnaXN0ZXJlZCE8L2E+ZGQCCw8PFgIfAAVJPGEgaHJlZj0gImphdmFzY3JpcHQ6c2V0Rm9jdXMoJ3R4dFBhc3N3b3JkJyk7Ij5QYXNzd29yZCBpcyBtYW5kYXRvcnkhPC9hPmRkZLFuksmAaQ+N5sw8K+rkFk3GqgSn"?/>
</div>

<script?type="text/javascript">
<!--
var?theForm?=?document.forms['form1'];

if?(!theForm)?
{
????theForm?=?document.form1;
}

function?__doPostBack(eventTarget,?eventArgument)?
{

????if?(!theForm.onsubmit?||?(theForm.onsubmit()?!=?false))?
{
????????theForm.__EVENTTARGET.value?=?eventTarget;
????????theForm.__EVENTARGUMENT.value?=?eventArgument;
????????theForm.submit();
????}
}
//?-->
</script>
<script?src="/Artech.ClickableValidationSummary/WebResource.axd?d=07ZNXubMk-rxUjn0jMywXg2&t=632969324944906146"?type="text/javascript"></script>
<script?src="/Artech.ClickableValidationSummary/WebResource.axd?d=5q3WmDnqxzNvEfUc_QbMe5qdQO1LUQ4P7mwuv6CrIMk1&t=632969324944906146"?type="text/javascript"></script>

<script?type="text/javascript">
<!--

function?WebForm_OnSubmit()?
{
if?(typeof(ValidatorOnSubmit)?==?"function"?&&?ValidatorOnSubmit()?==?false)?return?false;
return?true;
}
//?-->
</script>
????????<div>
????????????<table?cellpadding="0"?cellspacing="5px">
????????????????<tr>
????????????????????<td?colspan="2">
????????????????????????<div?id="vldLogin"?style="color:Red;display:none;">
</div>
????????????????????</td>
????????????????</tr>
????????????????<tr>
????????????????????<td?class="firstColumn">
????????????????????????User?Name:?<span?class="asterisk"> *</span></td>
????????????????????<td?class="secondColumn">
????????????????????????<input?name="txtUserName"?type="text"?id="txtUserName"?class="textbox"?/>
????????????????????????<span?id="rqfUserName"?style="color:Red;display:none;"></span>
????????????????????????<span?id="ctmUserName"?style="color:Red;display:none;"></span>
????????????????????</td>
????????????????</tr>
????????????????<tr>
????????????????????<td?class="firstColumn">
????????????????????????Password:?<span?class="asterisk"> *</span></td>
????????????????????<td?class="secondColumn">
????????????????????????<input?name="txtPassword"?type="password"?id="txtPassword"?class="textbox"?/>
????????????????????????<span?id="rqfPassword"?style="color:Red;display:none;"></span>
????????????????????</td>
????????????????</tr>
????????????????<tr>
????????????????????<td?colspan="2"?align="center">
????????????????????????<input?type="submit"?name="btnSignIn"?value="Sign?in"?onclick="javascript:WebForm_DoPostBackWithOptions(new?WebForm_PostBackOptions("btnSignIn",?"",?true,?"",?"",?false,?false))"?id="btnSignIn"?class="button"?/>
????????????????????????<input?type="submit"?name="ButtonCancel"?value="Cancel"?id="ButtonCancel"?class="button"?/>
????????????????????</td>
????????????????</tr>
????????????</table>
????????</div>????

<script?type="text/javascript">
<!--
var?Page_ValidationSummaries?=??new?Array(document.getElementById("vldLogin"));
var?Page_Validators?=??new?Array(document.getElementById("rqfUserName"),?document.getElementById("ctmUserName"),?document.getElementById("rqfPassword"));
//?-->
</script>


<script?type="text/javascript">
<!--
var?rqfUserName?=?document.all???document.all["rqfUserName"]?:?document.getElementById("rqfUserName");
rqfUserName.controltovalidate?=?"txtUserName";
rqfUserName.errormessage?=?"<a?href=?/"javascript:setFocus(/'txtUserName/');/">User?name?is?mandatory!</a>";
rqfUserName.display?=?"None";
rqfUserName.evaluationfunction?=?"RequiredFieldValidatorEvaluateIsValid";
rqfUserName.initialvalue?=?"";
var?ctmUserName?=?document.all???document.all["ctmUserName"]?:?document.getElementById("ctmUserName");
ctmUserName.controltovalidate?=?"txtUserName";
ctmUserName.errormessage?=?"<a?href=?/"javascript:setFocus(/'txtUserName/');/">Such?a?user?has?not?registered!</a>";
ctmUserName.display?=?"None";
ctmUserName.evaluationfunction?=?"CustomValidatorEvaluateIsValid";
var?rqfPassword?=?document.all???document.all["rqfPassword"]?:?document.getElementById("rqfPassword");
rqfPassword.controltovalidate?=?"txtPassword";
rqfPassword.errormessage?=?"<a?href=?/"javascript:setFocus(/'txtPassword/');/">Password?is?mandatory!</a>";
rqfPassword.display?=?"None";
rqfPassword.evaluationfunction?=?"RequiredFieldValidatorEvaluateIsValid";
rqfPassword.initialvalue?=?"";
//?-->
</script>
<div>
????<input?type="hidden"?name="__EVENTVALIDATION"?id="__EVENTVALIDATION"?value="/wEWBQL7uOMiAqXVsrMJArWptJELAsP3i5QHAv23gdwNI0m2v8hOJGGPTPLYqDLAkZE0nKU="?/>
</div>

<script?type="text/javascript">
<!--
var?Page_ValidationActive?=?false;

if?(typeof(ValidatorOnLoad)?==?"function")?
{
????ValidatorOnLoad();
}


function?ValidatorOnSubmit()?
{

????if?(Page_ValidationActive)?
{
????????return?ValidatorCommonOnSubmit();
????}

????else?
{
????????return?true;
????}
}
//?-->
</script>
????????</form>
</body>
</html>
我們從中提取對Validation有用的信息。
首先我們會看到有兩個JavaScript被引用:
< script? src ="/Artech.ClickableValidationSummary/WebResource.axd?d=07ZNXubMk-rxUjn0jMywXg2&t=632969324944906146" ?type ="text/javascript" ></ script >
< script? src ="/Artech.ClickableValidationSummary/WebResource.axd?d=5q3WmDnqxzNvEfUc_QbMe5qdQO1LUQ4P7mwuv6CrIMk1&t=632969324944906146" ?type ="text/javascript" ></ script >
這兩個JavaScript由ASP.NET生成。尤其內(nèi)容較多,在這里先不列出他們的內(nèi)容,等下面真正要使用到其中定義的JavaScript 在列出來。我們現(xiàn)在姑且稱它們?yōu)镴avaScript1和JavaScript2。
在下面一段JavaScript中,為3個Validator control定義了3個Client端的對象,對象的名稱和控件名稱同名,并設(shè)置相關(guān)的屬性:controltovalidate,errormessage,display,evaluationfunction。其中evaluationfunction為進行Validation的function的名稱.
< script? type ="text/javascript" >
<!--
var?rqfUserName?=?document.all???document.all["rqfUserName"]?:?document.getElementById("rqfUserName");
rqfUserName.controltovalidate?=?"txtUserName";
rqfUserName.errormessage?=?"<a?href=?/"javascript:setFocus(/'txtUserName/');/">User?name?is?mandatory!</a>";
rqfUserName.display?=?"None";
rqfUserName.evaluationfunction?=?"RequiredFieldValidatorEvaluateIsValid";
rqfUserName.initialvalue?=?"";
var?ctmUserName?=?document.all???document.all["ctmUserName"]?:?document.getElementById("ctmUserName");
ctmUserName.controltovalidate?=?"txtUserName";
ctmUserName.errormessage?=?"<a?href=?/"javascript:setFocus(/'txtUserName/');/">Such?a?user?has?not?registered!</a>";
ctmUserName.display?=?"None";
ctmUserName.evaluationfunction?=?"CustomValidatorEvaluateIsValid";
var?rqfPassword?=?document.all???document.all["rqfPassword"]?:?document.getElementById("rqfPassword");
rqfPassword.controltovalidate?=?"txtPassword";
rqfPassword.errormessage?=?"<a?href=?/"javascript:setFocus(/'txtPassword/');/">Password?is?mandatory!</a>";
rqfPassword.display?=?"None";
rqfPassword.evaluationfunction?=?"RequiredFieldValidatorEvaluateIsValid";
rqfPassword.initialvalue?=?"";
//?-->
</ script >
我們還發(fā)現(xiàn)通過Javascript定義了兩個Array對象:Page_ValidationSummaries和Page_Validators。這兩個Array用于保存Page中的所有的ValidationSummary和Validator control。
< script? type ="text/javascript" >
<!--
var?Page_ValidationSummaries?=??new?Array(document.getElementById("vldLogin"));
var?Page_Validators?=??new?Array(document.getElementById("rqfUserName"),?document.getElementById("ctmUserName"),?document.getElementById("rqfPassword"));
//?-->
</ script >
我們知道,所有的Validation操作都是在Click “Sign In” Button之后進行的。我們來看看,他是如何定義的:
< input? type ="submit" ?name ="btnSignIn" ?value ="Sign?in" ?onclick ="javascript:WebForm_DoPostBackWithOptions(new?WebForm_PostBackOptions("btnSignIn",?"",?true,?"",?"",?false,?false))" ?id ="btnSignIn" ?class ="button" ? />
通過onclick事件,我們可以看到,一個命名為WebForm_DoPostBackWithOptions的javascript function被調(diào)用,該function接收一個稱為WebForm_PostBackOptions類型的對象。該類型被定一個在JavaScript1中(還記得JavaScript1指的是什么嗎? 上溯到第三段)。下面是他的定義:
function ?WebForm_PostBackOptions(eventTarget,?eventArgument,?validation,?validationGroup,?actionUrl,?trackFocus,?clientSubmit)?
{
????this.eventTarget?=?eventTarget;
????this.eventArgument?=?eventArgument;
????this.validation?=?validation;
????this.validationGroup?=?validationGroup;
????this.actionUrl?=?actionUrl;
????this.trackFocus?=?trackFocus;
????this.clientSubmit?=?clientSubmit;
}
該對象具有這樣的表述的是關(guān)于Postback context的一些信息,比如:
-
eventTarget:Event觸發(fā)的control,當(dāng)前為” btnSignIn”。
-
eventArgument:Event額外的參數(shù), 當(dāng)前為””。
-
validation:是否進行Validation,當(dāng)前為true。
-
validationGroup:eventTarget 對應(yīng)的Validation group,這是ASP.NET 2.0的新特性,當(dāng)當(dāng)前為””,因為我沒有設(shè)置btnSignIn的ValidationGroup的property。
-
actionUrl:表單被提交的Url,就像asp中Form的action一樣。ASP.NET 1.x不提供cross-page的提交,在2.0中提供了此功能,當(dāng)前為””, 我沒有進行cross-page的提交。
-
trackFocus:是否進行焦點追蹤,當(dāng)前為false。
-
clientSubmit:是否通過form submit導(dǎo)致Postback,當(dāng)前為false。
我們再來看看WebForm_DoPostBackWithOptions,像WebForm_PostBackOptions一樣,該function同樣被定義在JavaScript1中。
function ?WebForm_DoPostBackWithOptions(options)?
{
????var?validationResult?=?true;

????if?(options.validation)?
{

????????if?(typeof(Page_ClientValidate)?==?'function')?
{
????????????validationResult?=?Page_ClientValidate(options.validationGroup);
????????}
????}

????if?(validationResult)?
{

????????if?((typeof(options.actionUrl)?!=?"undefined")?&&?(options.actionUrl?!=?null)?&&?(options.actionUrl.length?>?0))?
{
????????????theForm.action?=?options.actionUrl;
????????}

????????if?(options.trackFocus)?
{
????????????var?lastFocus?=?theForm.elements["__LASTFOCUS"];

????????????if?((typeof(lastFocus)?!=?"undefined")?&&?(lastFocus?!=?null))?
{

????????????????if?(typeof(document.activeElement)?==?"undefined")?
{
????????????????????lastFocus.value?=?options.eventTarget;
????????????????}

????????????????else?
{
????????????????????var?active?=?document.activeElement;

????????????????????if?((typeof(active)?!=?"undefined")?&&?(active?!=?null))?
{

????????????????????????if?((typeof(active.id)?!=?"undefined")?&&?(active.id?!=?null)?&&?(active.id.length?>?0))?
{
????????????????????????????lastFocus.value?=?active.id;
????????????????????????}

????????????????????????else?if?(typeof(active.name)?!=?"undefined")?
{
????????????????????????????lastFocus.value?=?active.name;
????????????????????????}
????????????????????}
????????????????}
????????????}
????????}
????}

????if?(options.clientSubmit)?
{
????????__doPostBack(options.eventTarget,?options.eventArgument);
????}
}
在開始的時候,調(diào)用Page_ClientValidate進行Client端的Validation。在這里執(zhí)行所有的Client端的Validation。我們來著重分析上面的javascript,看看具體的流程。Page_ClientValidate被定義在Javascript2中。
function ?Page_ClientValidate(validationGroup)?
{
????Page_InvalidControlToBeFocused?=?null;

????if?(typeof(Page_Validators)?==?"undefined")?
{
????????return?true;
????}
????var?i;

????for?(i?=?0;?i?<?Page_Validators.length;?i++)?
{
????????ValidatorValidate(Page_Validators[i],?validationGroup,?null);
????}
????ValidatorUpdateIsValid();
????ValidationSummaryOnSubmit(validationGroup);
????Page_BlockSubmit?=?!Page_IsValid;
????return?Page_IsValid;
}
上面的code中,首先通過Page_Validators判斷是否Page中定義了Validator control。我們在預(yù)先定義了Page_Validators Array(還記得我們之前介紹的兩個Array——Page_ValidationSummaries和Page_Validators嗎?)。雖有遍歷所有的Validator control,并調(diào)用ValidatorValidate方法執(zhí)行每個Validator control的Client端的Validation。我們進一步看看ValidatorValidate又是如何定義的(ValidatorValidate定義在Javascript2中):
function ?ValidatorValidate(val,?validationGroup,?event)?
{
????val.isvalid?=?true;

????if?((typeof(val.enabled)?==?"undefined"?||?val.enabled?!=?false)?&&?IsValidationGroupMatch(val,?validationGroup))?
{

????????if?(typeof(val.evaluationfunction)?==?"function")?
{
????????????val.isvalid?=?val.evaluationfunction(val);
????????????if?(!val.isvalid?&&?Page_InvalidControlToBeFocused?==?null?&&

????????????????typeof(val.focusOnError)?==?"string"?&&?val.focusOnError?==?"t")?
{
????????????????ValidatorSetFocus(val,?event);
????????????}
????????}
????}
????ValidatorUpdateDisplay(val);
}
首先通過IsValidationGroupMatch判斷Validator control的ValidationGroup是否和觸發(fā)Postaback的Control對應(yīng)的ValidationGroup相互匹配。因為只有在匹配的前提下才進行相關(guān)Validator control的validation。然后調(diào)用validator control的evaluationfunction function來進行validation。通過前面的分析,我們知道RequiredFieldValidator的evaluationfunction為RequiredFieldValidatorEvaluateIsValid,而CustomValidator的evaluationfunction為CustomValidatorEvaluateIsValid。我們來看看這兩個function是如何定義的。他們都定義在Javascript2中。
function ?RequiredFieldValidatorEvaluateIsValid(val)?
{
????return?(ValidatorTrim(ValidatorGetValue(val.controltovalidate))?!=???????ValidatorTrim(val.initialvalue))
}

function ?ValidatorGetValue(id)?
{
????var?control;
????control?=?document.getElementById(id);

????if?(typeof(control.value)?==?"string")?
{
????????return?control.value;
????}
????return?ValidatorGetValueRecursive(control);
}
function ?ValidatorGetValueRecursive(control)

{

????if?(typeof(control.value)?==?"string"?&&?(control.type?!=?"radio"?||?control.checked?==?true))?
{
????????return?control.value;
????}
????var?i,?val;

????for?(i?=?0;?i<control.childNodes.length;?i++)?
{
????????val?=?ValidatorGetValueRecursive(control.childNodes[i]);
????????if?(val?!=?"")?return?val;
????}
????return?"";
}

function ?ValidatorTrim(s)?
{
????var?m?=?s.match(/^/s*(/S+(/s+/S+)*)/s*$/);
????return?(m?==?null)???""?:?m[1];
}
function ?CustomValidatorEvaluateIsValid(val)?
{
????var?value?=?"";

????if?(typeof(val.controltovalidate)?==?"string")?
{
????????value?=?ValidatorGetValue(val.controltovalidate);
????????if?((ValidatorTrim(value).length?==?0)?&&

????????????((typeof(val.validateemptytext)?!=?"string")?||?(val.validateemptytext?!=?"true")))?
{
????????????return?true;
????????}
????}

????var?args?=?
{?Value:value,?IsValid:true?};

????if?(typeof(val.clientvalidationfunction)?==?"string")?
{
????????eval(val.clientvalidationfunction?+?"(val,?args)?;");
????}
????return?args.IsValid;
}
在ValidatorValidate中,當(dāng)我們通過調(diào)用各個Validator control的evaluationfunction來進行Client端的驗證后,對于沒有通過驗證的Validator control,通過調(diào)用ValidatorSetFocus設(shè)置相應(yīng)控件的焦點。在這里就不在深入探討了。接著通過調(diào)用ValidatorUpdateDisplay來根據(jù)我們制定的Display和不同瀏覽器,來設(shè)置Error message的顯示方式。
function ?ValidatorUpdateDisplay(val)?
{

????if?(typeof(val.display)?==?"string")?
{

????????if?(val.display?==?"None")?
{
????????????return;
????????}

????????if?(val.display?==?"Dynamic")?
{
????????????val.style.display?=?val.isvalid???"none"?:?"inline";
????????????return;
????????}
????}
????if?((navigator.userAgent.indexOf("Mac")?>?-1)?&&

????????(navigator.userAgent.indexOf("MSIE")?>?-1))?
{
????????val.style.display?=?"inline";
????}
????val.style.visibility?=?val.isvalid???"hidden"?:?"visible";
}
實際上到現(xiàn)在為止,所有的Validation工作已經(jīng)完成。我們來看看Error message是如何顯示的。所以我們要看看ValidatorUpdateDisplay的定義了。
分析完ValidatorValidate,我們在回到Page_ClientValidate上面。現(xiàn)在我們接著分析一下的執(zhí)行流程。通過調(diào)用ValidatorValidate執(zhí)行完各個Validator control的驗證后,接著調(diào)用的是ValidatorUpdateIsValid()和ValidationSummaryOnSubmit(validationGroup)。ValidatorUpdateIsValid通過遍歷每個Validator control來查看他們是否通過驗證,最終確定這個Page是否通過驗證。ValidationSummaryOnSubmit通過拼接字符串的形式在ValidationSummary顯示對應(yīng)的Error message。這正是我們可我們Error message寫成hyperlink的原因所在。
function ?ValidatorUpdateIsValid()?
{
????Page_IsValid?=?AllValidatorsValid(Page_Validators);
}

function ?AllValidatorsValid(validators)?
{

????if?((typeof(validators)?!=?"undefined")?&&?(validators?!=?null))?
{
????????var?i;

????????for?(i?=?0;?i?<?validators.length;?i++)?
{

????????????if?(!validators[i].isvalid)?
{
????????????????return?false;
????????????}
????????}
????}
????return?true;
}

function ?ValidationSummaryOnSubmit(validationGroup)?
{
????if?(typeof(Page_ValidationSummaries)?==?"undefined")
????????return;
????var?summary,?sums,?s;

????for?(sums?=?0;?sums?<?Page_ValidationSummaries.length;?sums++)?
{
????????summary?=?Page_ValidationSummaries[sums];
????????summary.style.display?=?"none";

????????if?(!Page_IsValid?&&?IsValidationGroupMatch(summary,?validationGroup))?
{
????????????var?i;

????????????if?(summary.showsummary?!=?"False")?
{
????????????????summary.style.display?=?"";

????????????????if?(typeof(summary.displaymode)?!=?"string")?
{
????????????????????summary.displaymode?=?"BulletList";
????????????????}

????????????????switch?(summary.displaymode)?
{
????????????????????case?"List":
????????????????????????headerSep?=?"<br>";
????????????????????????first?=?"";
????????????????????????pre?=?"";
????????????????????????post?=?"<br>";
????????????????????????end?=?"";
????????????????????????break;
????????????????????case?"BulletList":
????????????????????default:
????????????????????????headerSep?=?"";
????????????????????????first?=?"<ul>";
????????????????????????pre?=?"<li>";
????????????????????????post?=?"</li>";
????????????????????????end?=?"</ul>";
????????????????????????break;
????????????????????case?"SingleParagraph":
????????????????????????headerSep?=?"?";
????????????????????????first?=?"";
????????????????????????pre?=?"";
????????????????????????post?=?"?";
????????????????????????end?=?"<br>";
????????????????????????break;
????????????????}
????????????????s?=?"";

????????????????if?(typeof(summary.headertext)?==?"string")?
{
????????????????????s?+=?summary.headertext?+?headerSep;
????????????????}
????????????????s?+=?first;

????????????????for?(i=0;?i<Page_Validators.length;?i++)?
{

????????????????????if?(!Page_Validators[i].isvalid?&&?typeof(Page_Validators[i].errormessage)?==?"string")?
{
????????????????????????s?+=?pre?+?Page_Validators[i].errormessage?+?post;
????????????????????}
????????????????}
????????????????s?+=?end;
????????????????summary.innerHTML?=?s;
????????????????window.scrollTo(0,0);
????????????}

????????????if?(summary.showmessagebox?==?"True")?
{
????????????????s?=?"";

????????????????if?(typeof(summary.headertext)?==?"string")?
{
????????????????????s?+=?summary.headertext?+?"/r/n";
????????????????}
????????????????var?lastValIndex?=?Page_Validators.length?-?1;

????????????????for?(i=0;?i<=lastValIndex;?i++)?
{

????????????????????if?(!Page_Validators[i].isvalid?&&?typeof(Page_Validators[i].errormessage)?==?"string")?
{

????????????????????????switch?(summary.displaymode)?
{
????????????????????????????case?"List":
????????????????????????????????s?+=?Page_Validators[i].errormessage;

????????????????????????????????if?(i?<?lastValIndex)?
{
????????????????????????????????????s?+=?"/r/n";
????????????????????????????????}
????????????????????????????????break;
????????????????????????????case?"BulletList":
????????????????????????????default:
????????????????????????????????s?+=?"-?"?+?Page_Validators[i].errormessage;

????????????????????????????????if?(i?<?lastValIndex)?
{
????????????????????????????????????s?+=?"/r/n";
????????????????????????????????}
????????????????????????????????break;
????????????????????????????case?"SingleParagraph":
????????????????????????????????s?+=?Page_Validators[i].errormessage?+?"?";
????????????????????????????????break;
????????????????????????}
????????????????????}
????????????????}
????????????????alert(s);
????????????}
????????}
????}
}
2 Server side Validation
前面我們花了很大的篇幅介紹了Client端的Validation,通過介紹我們知道了,Client端的validation和Error message的顯示均由Javascript來完成?,F(xiàn)在我們來簡單看看Server 端的Validation。當(dāng)client 端__doPostBack被調(diào)用實現(xiàn)向Server端的Postback。具體的Postback可以參考我的文章:淺談ASP.NET的Postback。Validator Contro 的Server端的Validation,Error message直接通過Html顯示出來。
比如下面是一段CustomValidator的Validation。
protected ? void ?ctmUserName_ServerValidate( object ?source,?ServerValidateEventArgs?args)

????
{
????????if?(this.txtUserName.Text.Trim()?!=?"adm")

????????
{
????????????args.IsValid?=?false;
????????????return;
????????}
????????args.IsValid?=?true;
????}
如果上面的Validation沒有通過,最終render在client段的將有下面一段Html。

