第3章 HtmlHelper控件解析
在ASP.NET 3.5 MVC项目中,视图的开发需要用到HtmlHelper控件,这些控件都是通过各种扩展方法来实现的。
本章主要解析HtmlHelper控件7个大类中各个控件的主要使用方法,这些控件分别是控件BeginForm、BeginRouteForm和EndForm;控件CheckBox、Hidden、Password、RadioButton和TextBox;控件ActionLink和RouteLink;控件RenderPartial;控件DropDownList和ListBox;控件TextArea及控件ValidationMessage和ValidationSummary。
本章要点:
● HtmlHelper概述
● FormExtensions类
● InputExtensions类
● LinkExtensions类
● RenderPartialExtensions类
● SelectExtensions类
● TextAreaExtensions类
● ValidationExtensions类
3.1 HtmlHelper概述
3.1.1 HtmIHeIper类
HtmlHelper类位于命令System.Web.Mvc.Html之中,主要由7个静态类组成,它们分别是FormExtensions类、InputExtensions类、LinkExtensions类、SelectExtensions类、TextAreaExtensions类、ValidationExtensions类及RenderPartialExtensions类,它们的UML类图,如图3-1所示。
图3-1 HtmlHelper控件中的各个类
HtmlHelper类实现了ASP.NET 3.5 MVC项目中的各种控件,其内部是通过各种扩展方法来实现的,可以满足一般项目中视图的开发。
开发者根据自己的需求,可以通过扩展方法开发自己所需要的控件,在第4章中,就实现了一个功能较为完善的数据显示GridView控件。
3.1.2 视图中的HTML属性
为方便开发者使用HtmlHelper控件,在视图ViewPage类中(该类的UML类图,如图3-2所示),专门设置了一个HTML属性,代码如下:
图3-2 ViewPage类的UML类图
public HtmlHelper Html { get; set; }
从上述代码中可以看出,HTML属性就是HtmlHelper的类型,因此在视图页面中通过HTML属性调用实现控件的相关扩展方法。
3.2 FormExtensions类
FormExtensions类的UML类图,如图3-3所示。
图3-3 FormExtensions的UML类图
FormExtensions类是一个静态类,其中定义了3种类型的扩展方法,供开发者在视图中设置表单和表单路由的定义,它们分别是BeginForm、BeginRouteForm、EndForm,以下分别予以说明。
3.2.1 BeginForm
BeginForm扩展方法,主要实现表单定义的开始部分,定义了13个重载的扩展方法供开发者使用,见表3-1。
表3-1 BeginForm()的13个重载方法
对于第2个重载方法,可以设置如下代码:
Html.BeginForm(new {action="action", controller="controller", id="2" })
在上述代码中,设置了路由值的一个实例化对象,对应输出的HTML语句如下:
<form action="/controller/action/2" method="post">
从上述的HTML语句中可以看出,通过所设置的action值、controller值及id值,根据视图页面所在项目的路由设置,构造了“/controller/action/2”的路由,而默认的表单提交方法,则为post方法。
关于表单提交方法的设置,只能通过对应的参数FormMethod.Post或者FormMethod.Get来实现,对于第10个重载方法,可以设置如下代码:
Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { method="Get" } )
在上述代码中,在第3个参数中设置了FormMethod.Post值,在第4个参数中又设置了一个method值,对应输出的HTML语句如下:
<form action="/ControllerName/ActionName" method="post">
由此可见,后面的第4个参数所设置的method值是无效的,也就是说,FormMethod.Post值的优先级别最高。
这里需要说明的是,可以在多个地方设置action参数,不过htmlAttributes中所设置的优先级别最高,例如第12个重载方法的如下代码:
Html.BeginForm("actionName1", "Controller1", FormMethod.Post, new { action = "/ Controller/actionName "})
在上述代码中,通过第一个参数设置了action参数为actionName1,还通过实例化htmlAttributes对象设置了action参数,对应输出的HTML语句如下:
<form action="/Controller/actionName" method="post">
从上述语句可以看出,后面的第4个参数中所设置的action参数级别最高。
对于第13个重载方法,可以设置如下代码:
Html.BeginForm("ActionName", "ControllerName", new RouteValueDictionary { { "id", 123 } }, FormMethod.Post, new RouteValueDictionary { { "class", "cssName" } })
在上述代码中,设置了路由值字典类型的id值和class值,对应输出的HTML语句如下:
<form action="/ControllerName/ActionName/123" class="cssName" method="post">
由此可见,可以通过IDictionary<string, object>的实例化对象设置相关的属性值。
3.2.2 BeginRouteForm
BeginRouteForm扩展方法,主要实现表单定义的开始部分,其中以路由方式设置action的值,定义了12个重载的扩展方法供开发者使用,见表3-2。
表3-2 BeginRouteForm()的12个重载方法
对于第1个重载方法,可以设置如下代码:
Html.BeginRouteForm(new { action="action" })
在上述代码中,设置了一个action值,对应输出的HTML语句如下:
<form action="/Home/action" method="post">
如果使用对应的BeginForm方法,上述的action为“action”,而不是“/Home/action”,这就是BeginForm与BeginRouteForm之间的区别。
对于第2个重载方法,可以设置如下代码:
Html.BeginRouteForm(new {controller="controller", action="action" })
在上述代码中,设置了一个controller值和一个action值,对应输出的HTML语句如下:
<form action="/controller/action" method="post">
其他重载方法的使用与BeginForm中的类似,这里不再重复。
3.2.3 EndForm
EndForm扩展方法,主要实现表单定义的结束部分,开发者在视图中设置如下代码:
Html.EndForm()
上述代码所对应的HTML语句如下:
</form>
在实际的应用开发中,开发者还可以使用using语句,而不需要书写上述的EndForm扩展方法。
3.3 lnputExtensions类
InputExtensions类的UML类图,如图3-4所示。
图3-4 InputExtensions的UML类图
InputExtensions类中定义了5种类型的扩展方法,供开发者在视图中设置CheckBox控件、Hidden控件、Password控件、RadioButton控件及TextBox控件,以下分别予以说明。
3.3.1 CheckBox
CheckBox扩展方法,主要实现复选框控件,定义了6个重载的扩展方法供开发者使用,见表3-3。
表3-3 CheckBox()的6个重载方法
对于第3个重载方法,可以设置如下的复选框,见代码清单3-1。
代码清单3-1设置复选框的实现代码
1: <%=Html.BeginForm ("CheckBox", "Home") %> 2: <fieldset> 3: <legend>设置字体:</legend> 4: 5: <%=Html.CheckBox("MyCheckBox1", true, new { id="checkBox1" })%> 6: <label for="checkBox1">黑体</label> 7: 8: <%=Html.CheckBox ("MyCheckBox2", false, new { id="checkBox2" }) %> 9: <label for="checkBox2">斜体</label> 10: 11: <br/><br/> 12: <input type="submit" value="Submit" /> 13: 14: </fieldset> 15: <% Html.EndForm(); %>
在上述代码中,第5行、第8行分别设置了两个复选框控件MyCheckBox1、MyCheckBox2,为方便浏览者选择相关的复选框控件,第6行、第9行还分别设置了相关的标签。
运行上述代码,上述复选框控件的设置代码所对应的HTML语句如下:
<input checked="checked" id="checkBox1" name="MyCheckBox1" type="checkbox" value="true" /> <input name="MyCheckBox1" type="hidden" value="false" /> <input id="checkBox2" name="MyCheckBox2" type="checkbox" value="true" /> <input name="MyCheckBox2" type="hidden" value="false" />
从上述代码中可以看出,对于每一个CheckBox控件,ASP.NET 3.5 MVC框架都另外生成了一个隐藏的对应控件,对于MyCheckBox1复选框控件,生成了一个名称为“MyCheckBox1”的隐藏控件;对于MyCheckBox2复选框控件,则生成了一个名称为“MyCheckBox2”的隐藏控件。因此在控制器中检测复选框控件的状态,可以使用如下代码:
public ActionResult CheckBox(FormCollection formCollection) { bool myCheckBox1=formCollection[0].Contains ("true"); bool myCheckBox2 = formCollection["MyCheckBox2"].Contains("true"); ViewData["checkBox1"] = myCheckBox1; ViewData["checkBox2"] = myCheckBox2; return View(); }
在上述代码中,通过表单集合formCollection的索引或者键,可以获得复选框控件的状态值字符串,该字符串是“true false”或者“false false”(每一个CheckBox控件还对应一个false状态的隐藏控件),因此调用了Contains()方法来判断该复选框控件是否被选择。
3.3.2 Hidden
Hidden扩展方法主要定义表单中有关变量的隐藏数值,定义了4个重载的扩展方法供开发者使用,见表3-4。
表3-4 Hidden()的4个重载方法
对于第1个重载方法,可以设置如下代码:
Html.Hidden("testName")
在上述代码中,设置了一个名称为“testName”的隐藏输入变量,对应输出的HTML语句如下:
<input id="testName" name="testName" type="hidden" value="" />
其中隐藏变量“testName”的值为空白。
对于第2个重载方法,可以设置如下代码:
Html.Hidden("testName", 123)
上述代码所对应输出的HTML语句如下:
<input id="testName" name="testName" type="hidden" value="123" />
在上述代码中,设置了一个名称为“testName”的隐藏输入变量,而且该变量的数值为123。
3.3.3 Password
Password扩展方法主要实现输入密码的文本框,定义了4个重载的扩展方法供开发者使用,见表3-5。
表3-5 Password()的4个重载方法
对于第1个重载方法,可以设置如下代码:
Html.Password ("MyPassword")
在上述代码中,设置了一个名称为“MyPassword”的密码文本框,对应输出的HTML语句如下:
<input id="MyPassword" name="MyPassword" type="password" />
其中设置了输入类型为密码、名称为“MyPassword”的密码文本框。
对于第2个重载方法,可以设置如下代码:
Html.Password("Password", 123)
上述代码所对应输出的HTML语句如下:
<input id="Password" name="Password" type="password" value="123" />
在上述代码中,设置了一个名称为“Password”的密码文本框,而且该文本框中的密码数值为123。
3.3.4 RadioButton
RadioButton扩展方法主要实现单选按钮控件,一共定义了6个重载的扩展方法供开发者使用,见表3-6。
表3-6 RadioButton()的6个重载方法
对于第5个重载方法,可以设置如下的单选按钮,见代码清单3-2。
代码清单3-2设置单选按钮的实现代码
1: <% using (Html.BeginForm("RadioButton", "Home")) 2: { %> 3: <fieldset> 4: <legend>设置字号:</legend> 5: <% =Html.RadioButton("MyRadioButton", "MyValue1", true, new { id = "MyRadioButton1" })%> 6: <label for="MyRadioButton1">10号</label> 7: 8: <%=Html.RadioButton("MyRadioButton", "MyValue2", new { id = "MyRadioButton2" })%> 9: <label for="MyRadioButton2">20号</label> 10: 11: <br/><br/> 12: <input type="submit" value="RadioButton" /> 13: </fieldset> 14: <% }
在上述代码中,第5行、第8行分别设置了两个单选按钮MyRadioButton1、MyRadioButton2,为方便浏览者选择相关的单选按钮控件,第6行、第9行还分别设置了相关的标签。
运行上述代码,上述单选按钮的设置代码所对应的HTML语句如下:
<input checked="checked" id="MyRadioButton1" name="MyRadioButton" type="radio" value="MyValue1" /> <input id="MyRadioButton2" name="MyRadioButton" type="radio" value="MyValue2" />
从上述代码中可以看出,所有的单选按钮的名称是相同的,但是可以设置不同的id。在控制器中检测单选按钮的状态,可以使用如下代码:
public ActionResult RadioButton(FormCollection formCollection) { string radioButton1 = formCollection[0]; string radioButton2 = formCollection["MyRadioButton"]; ViewData["MyRadioButton"] = radioButton1 ; return View(); }
在上述代码中,通过表单集合formCollection的索引或者键,可以获得被选择单选按钮的设定值,如果MyRadioButton1被选择,则该值应该为“MyValue1”;如果MyRadioButton2被选择,则该值应该为“MyValue2”。
3.3.5 TextBox
TextBox扩展方法主要实现输入的单行文本框,定义了4个重载的扩展方法供开发者使用,见表3-7。
表3-7 TextBox()的4个重载方法
对于第1个重载方法,可以设置如下代码:
Html.TextBox("Message")
在上述代码中,设置了一个名称为“Message”的文本框,对应输出的HTML语句如下:
<input id="Message" name="Message" type="text" value="Welcome to ASP.NET 3.5 MVC! " />
这里需要说明的是,文本框的名称为“Message”,没有设置该文本框的取值,视图将会尝试在Model中或者被保存的字典中,获得跟“Message”相对应的值,由于默认的MVC网站中设置了如下代码:
ViewData["Message"] = "Welcome to ASP.NET 3.5 MVC! ";
因此,在上述的文本框中,value的取值为“Welcome to ASP.NET 3.5 MVC! ”。
同样对于第1个重载方法,如果设置如下代码:
Html.TextBox("myTextBox")
则代码所对应输出的HTML语句如下:
<input id="myTextBox" name="myTextBox" type="text" value="" />
在上述代码中,设置了一个名称为“myTextBox”的文本框,而且该文本框中的默认值为空白。
对于第3个重载方法,可以设置如下代码:
Html.TextBox("myTextBox", null, new { size=50 })
上述代码所对应输出的HTML语句如下:
<input id="myTextBox" name="myTextBox" size="50" type="text" value="" />
在上述代码中,设置了一个名称为“myTextBox”的文本框,而且该文本框中的宽度为50。
3.4 LinkExtensions类
LinkExtensions类的UML类图,如图3-5所示。
图3-5 LinkExtensions的UML类图
LinkExtensions类中定义了两种类型的扩展方法,供开发者在视图中设置相关的链接,它们分别是ActionLink和RouteLink,以下分别予以说明。
3.4.1 ActionLink
ActionLink扩展方法主要实现一个链接,定义了10个重载的扩展方法供开发者使用,见表3-8。
表3-8 ActionLink()的10个重载方法
对于第1个重载方法,可以设置如下代码:
Html.ActionLink ("testLink", "actionName")
上述代码所对应输出的HTML语句如下:
<a href="/Home/actionName">testLink</a>
在上述代码中,设置了一个“testLink”链接,该链接地址指向“/Home/actionName”,其中Home是控制视图页面的控制器名称。
对于第6个重载方法,可以设置如下代码:
Html.ActionLink ("testLink", "actionName", "controller")
上述代码所对应输出的HTML语句如下:
<a href="/controller/actionName">testLink</a>
在上述代码中,设置了一个“testLink”链接,该链接地址指向“/controller/actionName”,此时的控制器为用户所设置。
3.4.2 RouteLink
RouteLink扩展方法主要实现一个链接,定义了10个重载的扩展方法供开发者使用,见表3-9。
表3-9 RouteLink()的10个重载方法
对于第1个重载方法,可以设置如下代码:
Html.RouteLink("routLink", new{ controller="controller", action="action"} )
上述代码所对应输出的HTML语句如下:
<a href="/controller/action">routLink</a>
在上述代码中,设置了一个“routeLink”链接,该链接地址指向“/controller/action”,这里与有关的ActionLink的使用效果完全相同。
对于第3个重载方法,可以设置如下代码:
Html.RouteLink("routLink", "default", new { id=100 } )
上述代码所对应输出的HTML语句如下:
<a href="/Home/Index/100">routLink</a>
在上述代码中,设置了一个“routeLink”链接,其中调用了名称为“default”的路由,该链接地址指向“/Home/Index/100”。
3.5 RenderPartialExtensions类
RenderPartialExtensions类的UML类图,如图3-6所示。
图3-6 RenderPartialExtensions的UML类图
RenderPartialExtensions类是一个静态类,其中定义了4个重载的扩展方法RenderPartial,以便开发者在视图中显示相关的用户控件,见表3-10。
表3-10 RenderPartial()的4个重载方法
对于第1个重载方法,可以设置如下代码:
<% Html.RenderPartial("List" ); %>
在上述代码中,需要被显示的用户控件为List.ascx,该用户控件主要用来显示数据表Categories中的数据。
上述代码的运行界面如图3-7所示。
图3-7 显示用户控件的运行界面
在ASP.NET 3.5 MVC项目中,通过使用RenderPartial扩展方法来显示用户控件,便于视图页面的封装。
3.6 SelectExtensions类
SelectExtensions类的UML类图,如图3-8所示。
图3-8 SelectExtensions的UML类图
SelectExtensions类是一个静态类,其中设置了3种类型的扩展方法,以便开发者在视图中设置相关的界面控件,它们分别是DropDownList控件和ListBox控件。
3.6.1 DropDownList
DropDownList扩展方法主要实现一个下拉列表框,定义了8个重载的扩展方法供开发者使用,见表3-11。
表3-11 DropDownList()的8个重载方法
对于第1个重载方法,可以设置如下的下拉列表框,见代码清单3-3。
代码清单3-3设置拉列表框的实现代码
1: <%=Html.BeginForm ("DropDownList", "Home") %> 2: <fieldset> 3: <legend>选择产品目录:</legend> 4: <%= Html.DropDownList("CategoryID") %> 5: <br/><br/> 6: <input type="submit" value="DropDownList" /> 7: 8: </fieldset> 9: <% Html.EndForm(); %>
在上述代码中,第4行设置了一个名称为“CategoryID”的下拉列表框,需要说明的是,在该列表框中的选项等数据是SelectList类(位于命名空间System.Web.Mvc之中)的实例化对象。
设置下拉列表框数据的代码,见代码清单3-4。
代码清单3-4设置下拉列表框数据的实现代码
1: public ActionResult Index() 2: { 3: NorthWindDataContext db = new NorthWindDataContext(); 4: 5: ViewData["Message"] = "Welcome to ASP.NET 3.5 MVC! "; 6: 7: ViewData["CategoryID"] = new SelectList(db.Categories , "CategoryName", "CategoryName"); 8: 9: return View(); 10: }
在上述代码中,第3行获得LINQ to SQL数据上下文类NorthWindDataContext的一个实例化对象db;第7行设置了SelectList类的实例化对象,其中输入了3个参数,第1个参数是被选项数据db.Categories;第2个参数是被选项的数据字段CategoryName;第3个参数是在下拉列表框中显示的数据选项。需要说明的是,这里之所以将被选项数据和列表数据都设置为“CategoryName”,主要是方便获得浏览者所选择下拉列表框中的数据。
代码清单3-3所对应输出的HTML语句如下:
<select id="CategoryID" name="CategoryID"> <option value="Beverages">Beverages</option> <option value="Condiments">Condiments</option> <option value="Confections">Confections</option> <option value="Dairy Products">Dairy Products</option> <option value="Grains/Cereals">Grains/Cereals</option> <option value="Meat/Poultry">Meat/Poultry</option> <option value="Produce">Produce</option> <option value="Seafood">Seafood</option> <option value="Test">Test</option> </select>
从上述代码中可以看出,如果浏览者选择了“Seafood”,那么被选择的“Value”也是“Seafood”,这样便于直接获得下拉列表框中被选择的数据,而不需要数据转换。
在控制器中检测下拉列表框被选择的数据,可以使用如下代码:
public ActionResult DropDownList(FormCollection formCollection) { string dropDownList1 = formCollection[0]; string dropDownList2 = formCollection["CategoryID"]; NorthWindDataContext db = new NorthWindDataContext(); ViewData["CategoryID"] = new SelectList(db.Categories, "CategoryName", "CategoryName", dropDownList1); return View(); }
在上述代码中,通过表单集合formCollection的索引或者键,可以获得被选择下拉列表框的设定值,然后将该值作为已经被选择的数据项,构造新的SelectList类的实例化对象,以便在视图中显示用户已经被选择的下拉列表框。
3.6.2 ListBox
ListBox扩展方法主要实现一个列表框,其中定义了4个重载的扩展方法供开发者使用,见表3-12。
表3-12 ListBox()的4个重载方法
对于第1个重载方法,可以设置如下的列表框,见代码清单3-5。
代码清单3-5设置列表框的实现代码
1: <% Html.BeginForm("ListBox", "Home"); %> 2: <fieldset> 3: <legend>选择产品目录:</legend> 4: <%= Html.ListBox ("CategoryID") %> 5: <br/><br/> 6: <input type="submit" value="ListBox" /> 7: 8: </fieldset> 9: <% Html.EndForm(); %>
在上述代码中,第4行设置了一个名称为“CategoryID”的列表框,需要说明的是,在该列表框中的选项等数据是MultiSelectList(位于命名空间System.Web.Mvc之中)类或者SelectList类的实例化对象。
设置列表框数据的代码与下拉列表框一样,这里不再重复。
上述代码所对应输出的HTML语句如下:
<select id="CategoryID" multiple="multiple" name="CategoryID"> <option value="Beverages">Beverages</option> <option value="Condiments">Condiments</option> <option value="Confections">Confections</option> <option value="Dairy Products">Dairy Products</option> <option value="Grains/Cereals">Grains/Cereals</option> <option value="Meat/Poultry">Meat/Poultry</option> <option value="Produce">Produce</option> <option value="Seafood">Seafood</option> <option value="Test">Test</option> </select>
从上述代码中可以看出,DropDownList控件只能选择一个选项,而ListBox控件则可以选择多个选项,因此multiple属性被设置为“multiple”。
在控制器中检测单列表框被选择的数据,可以使用如下代码:
public ActionResult ListBox(FormCollection formCollection) { var dropDownList1 = formCollection[0].Split(' , ' ).AsEnumerable() ; var dropDownList2 = formCollection["CategoryID"].Split(' , ' ). AsEnumerable () ; NorthWindDataContext db = new NorthWindDataContext(); ViewData["CategoryID"] = new MultiSelectList (db.Categories, "CategoryName", "CategoryName", dropDownList1); return View(); }
在上述代码中,通过表单集合formCollection的索引或者键,可以获得被选择列表框的设定值。这里需要说明的是,由于在列表框中,可以选择多个数据选项,所获得被选择的设定值是以冒号分开的字符串数据,因此通过Split()方法将多个选项转换为相关的字符串数组之后,然后再转换为IEnumerable类型的可查询数据。
在构建新的ListBox列表框控件时,这里设置了MultiSelectList类的实例化对象,其中输入了4个参数,第1个参数是被选项数据db.Categories;第2个参数是被选项的数据字段CategoryName;第3个参数是在列表框中显示的数据选项;第4个参数是已经被选择的数据选项。
3.7 TextAreaExtensions类
TextAreaExtensions类的UML类图,如图3-9所示。
图3-9 TextAreaExtensions的UML类图
TextAreaExtensions类中定义了8种重载的扩展方法TextArea,以便开发者在视图中设置TextArea控件,见表3-13。
表3-13 TextArea()的8个重载方法
对于第2个重载方法,可以设置如下代码:
Html.TextArea ("textArea", new { rows=5, cols=10 })
上述代码所对应输出的HTML语句如下:
<textarea cols="10" id="textArea" name="textArea" rows="5"></textarea>
在上述代码中,设置了一个名称为“textArea”的多行文本框,该文本框被设置为5行、10列。
对于第4个重载方法,可以设置如下代码:
Html.TextArea ("textArea", "Test data")
上述代码所对应输出的HTML语句如下:
<textarea cols="20" id="textArea" name="textArea" rows="2"> Test data</textarea>
在上述代码中,设置了一个名称为“textArea”的多行文本框,其中文本框中的内容为“Test data”,文本框被默认设置为2行、20列。
3.8 ValidationExtensions类
ValidationExtensions类的UML类图,如图3-10所示。
图3-10 ValidationExtensions的UML类图
ValidationExtensions类是一个静态类,其中定义了两种类型的扩展方法,以便让开发者实现相关控件的输入验证,这些验证控件分别是ValidationMessage控件和ValidationSummary控件。
3.8.1 VaIidationMessage
在ValidationExtensions类中,定义了6种重载的ValidationMessage扩展方法,以便开发者在视图中设置相关控件的输入验证,见表3-14。
表3-14 ValidationMessage()的6个重载方法
对于第3个重载方法,可以设置如下代码:
<%= Html.TextBox("ProductName") %> <%= Html.ValidationMessage("ProductName", "*") %>
在上述代码中,设置了验证信息控件ValidationMessage的模型名称为“ProductName”,而验证信息则为星号“*”,表明如果文本框中的输入信息为空白,则此时的文本框会变成红色,并在文本框的右边出现一个星号“*”提示信息,说明输入信息有误。
3.8.2 VaIidationSummary
在ValidationExtensions类中,定义了4种重载的ValidationSummary扩展方法,以便开发者在视图中设置相关控件的输入验证摘要,见表3-15。
表3-15 ValidationSummary()的4个重载方法
设置验证信息控件ValidationMessage及验证摘要控件ValidationSummary的实现代码,见代码清单3-6。
代码清单3-6设置验证控件的实现代码
1: <%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %> 2: 3: <% using (Html.BeginForm()) {%> 4: 5: <fieldset> 6: <legend>Fields</legend> 7: <p> 8: <label for="ProductName">ProductName:</label> 9: <%= Html.TextBox("ProductName") %> 10: <%= Html.ValidationMessage("ProductName", "*") %> 11: </p> 12: <p> 13: <label for="Description">Description:</label> 14: <%= Html.TextBox("Description") %> 15: <%= Html.ValidationMessage("Description", "*") %> 16: </p> 17: <p> 18: <label for="UnitPrice">UnitPrice:</label> 19: <%= Html.TextBox("UnitPrice") %> 20: <%= Html.ValidationMessage("UnitPrice", "*") %> 21: </p> 22: <p> 23: <input type="submit" value="Create" /> 24: </p> 25: </fieldset> 26: <% } %>
在上述代码中,第1行设置了验证摘要控件ValidationSummary,该控件一般设置在表单(第3行到第26行)的外部,而在表单的内部,则分别设置了3个验证信息控件ValidationMessage,分别绑定相关的3个文本框控件。
在控制器中,处理上述表单的Create()动作方法的实现代码,见代码清单3-7。
代码清单3-7 Create()动作方法的实现代码
1: [AcceptVerbs(HttpVerbs.Post)] 2: public ActionResult Create(MyProducts productsToCreate ) 3: { 4: NorthWindDataContext db = new NorthWindDataContext(); 5: 6: if (String.IsNullOrEmpty(productsToCreate.ProductName)) 7: { 8: ModelState.AddModelError("ProductName", "You must specify a ProductName."); 9: } 10: 11: if (String.IsNullOrEmpty(productsToCreate.Description)) 12: { 13: ModelState.AddModelError("Description", "You must specify a description."); 14: } 15: 16: if (ModelState.IsValid) 17: { 18: db.MyProducts.InsertOnSubmit(productsToCreate); 19: db.SubmitChanges(); 20: 21: return RedirectToAction("ListMyProduct"); 22: } 23: 24: return View(productsToCreate ); 25: }
在上述代码中,第4行创建LINQ to SQL的数据上下文类NorthWindDataContext的实例化对象db;第6行到第9行添加模型的异常信息,如果被发送表单中的ProductName文本框信息为空白,则会提示相关异常信息;第11行到第14行同样添加模型的异常信息,如果被发送表单中的Description文本框信息为空白,则会提示相关的异常信息;第16行到第22行设置添加相关信息到数据表中,而实现这一操作的前提是提交的表单模型中不含有任何异常信息,否则就会执行第24行语句,在页面中提示输入的异常信息。
代码清单3-7的运行界面如图3-11所示。
图3-11 代码清单3-7的运行界面
在图3-11中,如果在“ProductName”文本框及“Description”文本框中不输入任何内容,而在“UnitPrice”文本框中输入“abc”字符串(该字段应该输入数值),然后单击“Create”按钮。由于3个文本框中的输入都会出现异常,2个文本框的输入内容为空白,一个文本框的类型有误,此时打开如图3-12所示的运行界面。
从图3-12中可以看出,在3个文本框中都出现了异常,而在验证摘要控件ValidationSummary的异常信息中,只显示了2个文本框的异常信息,如“You must specify a ProductName.”和“You must specify a description.”,并没有显示UnitPrice文本框中的出错信息。
图3-12 输入异常后的运行界面
为什么在UnitPrice文本框中的出错信息没有显示在ValidationSummary的异常摘要信息中呢?请仔细阅读代码详解中的代码清单3-8。
ValidationExtensions类的实现代码,见代码清单3-8。
代码清单3-8 ValidationExtensions类的实现代码
1: public static class ValidationExtensions 2: { 3: private static string _resourceClassKey; 4: 5: public static string ResourceClassKey 6: { 7: get { return _resourceClassKey ? ? String.Empty; } 8: 9: set { _resourceClassKey = value; } 10: } 11: 12: private static string GetInvalidPropertyValueResource( HttpContextBase httpContext) 13: { 14: string resourceValue = null; 15: if (! String.IsNullOrEmpty(ResourceClassKey) && (httpContext ! = null)) 16: { 17: resourceValue = httpContext.GetGlobalResourceObject( ResourceClassKey, "InvalidPropertyValue", CultureInfo.CurrentUICulture) as string; 18: } 19: return resourceValue ? ? MvcResources. Common_ValueNotValidForProperty; 20: } 21: 22: private static string GetUserErrorMessageOrDefault(HttpContextBase httpContext, ModelError error, ModelState modelState) 23: { 24: if (! String.IsNullOrEmpty(error.ErrorMessage)) 25: { 26: return error.ErrorMessage; 27: } 28: if (modelState == null) 29: { 30: return null; 31: } 32: 33: string attemptedValue = (modelState.Value ! = null) ? modelState.Value.AttemptedValue : null; 34: return String.Format(CultureInfo.CurrentCulture, GetInvalidPropertyValueResource(httpContext), attemptedValue); 35: } 36: 37: public static string ValidationMessage(this HtmlHelper htmlHelper, string modelName) 38: { 39: return ValidationMessage(htmlHelper, modelName, (object)null /* htmlAttributes */); 40: } 41: 42: public static string ValidationMessage(this HtmlHelper htmlHelper, string modelName, object htmlAttributes) 43: { 44: return ValidationMessage(htmlHelper, modelName, new RouteValueDictionary(htmlAttributes)); 45: } 46: 47: public static string ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage) 48: { 49: return ValidationMessage(htmlHelper, modelName, validationMessage, (object)null /* htmlAttributes */); 50: } 51: 52: public static string ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage, object htmlAttributes) 53: { 54: return ValidationMessage(htmlHelper, modelName, validationMessage, new RouteValueDictionary(htmlAttributes)); 55: } 56: 57: public static string ValidationMessage(this HtmlHelper htmlHelper, string modelName, IDictionary<string, object> htmlAttributes) 58: { 59: return ValidationMessage(htmlHelper, modelName, null /* validationMessage */, htmlAttributes); 60: } 61: 62: public static string ValidationMessage(this HtmlHelper htmlHelper, string modelName, string validationMessage, IDictionary<string, object> htmlAttributes) 63: { 64: if (modelName == null) 65: { 66: throw new ArgumentNullException("modelName"); 67: } 68: 69: if (! htmlHelper.ViewData.ModelState.ContainsKey(modelName)) 70: { 71: return null; 72: } 73: 74: ModelState modelState = htmlHelper.ViewData.ModelState[modelName]; 75: 76: ModelErrorCollection modelErrors = (modelState == null) ? null : modelState.Errors; 77: ModelError modelError = ((modelErrors == null) —— (modelErrors.Count == 0)) ? null : modelErrors[0]; 78: 79: if (modelError == null) 80: { 81: return null; 82: } 83: 84: TagBuilder builder = new TagBuilder("span"); 85: builder.MergeAttributes(htmlAttributes); 86: builder.MergeAttribute("class", HtmlHelper.ValidationMessageCssClassName); 87: builder.SetInnerText(String.IsNullOrEmpty(validationMessage) ? GetUserErrorMessageOrDefault(htmlHelper.ViewContext. HttpContext, modelError, modelState) : validationMessage); 88: 89: return builder.ToString(TagRenderMode.Normal); 90: } 91: 92: public static string ValidationSummary(this HtmlHelper htmlHelper) 93: { 94: return ValidationSummary(htmlHelper, null /* message */); 95: } 96: 97: public static string ValidationSummary(this HtmlHelper htmlHelper, string message) 98: { 99: return ValidationSummary(htmlHelper, message, (object)null /* htmlAttributes */); 100: } 101: 102: public static string ValidationSummary(this HtmlHelper htmlHelper, string message, object htmlAttributes) 103: { 104: return ValidationSummary(htmlHelper, message, new RouteValueDictionary(htmlAttributes)); 105: } 106: 107: public static string ValidationSummary(this HtmlHelper htmlHelper, string message, IDictionary<string, object> htmlAttributes) 108: { 109: if (htmlHelper.ViewData.ModelState.IsValid) 110: { 111: return null; 112: } 113: 114: string messageSpan; 115: if (! String.IsNullOrEmpty(message)) 116: { 117: TagBuilder spanTag = new TagBuilder("span"); 118: spanTag.MergeAttributes(htmlAttributes); 119: spanTag.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName); 120: spanTag.SetInnerText(message); 121: messageSpan = spanTag.ToString(TagRenderMode.Normal) + Environment.NewLine; 122: } 123: else 124: { 125: messageSpan = null; 126: } 127: 128: StringBuilder htmlSummary = new StringBuilder(); 129: TagBuilder unorderedList = new TagBuilder("ul"); 130: unorderedList.MergeAttributes(htmlAttributes); 131: unorderedList.MergeAttribute("class", HtmlHelper.ValidationSummaryCssClassName); 132: 133: foreach (ModelState modelState in htmlHelper.ViewData.ModelState.Values) 134: { 135: foreach (ModelError modelError in modelState.Errors) 136: { 137: string errorText = GetUserErrorMessageOrDefault( htmlHelper.ViewContext.HttpContext, modelError, null /* modelState */); 138: if (! String.IsNullOrEmpty(errorText)) 139: { 140: TagBuilder listItem = new TagBuilder("li"); 141: listItem.SetInnerText(errorText); 142: htmlSummary.AppendLine(listItem.ToString( TagRenderMode.Normal)); 143: } 144: } 145: } 146: 147: unorderedList.InnerHtml = htmlSummary.ToString(); 148: 149: return messageSpan + unorderedList.ToString(TagRenderMode.Normal); 150: } 151: }
在上述代码中,第3行设置了一个私有的_resourceClassKey属性,用于表示个性化资源文件的名称;第5行到第10行则实现了_resourceClassKey属性的读写器;第12行到第20行所设置的GetInvalidPropertyValueResource()方法,主要实现从资源文件中输出相关的异常信息。
第22行到第35行所设置的GetUserErrorMessageOrDefault()方法,主要实现从模型异常类ModelError中输出异常信息ErrorMessage、模型状态ModelState中得到出现异常的数据字段attemptedValue(第33行)。
第37行到第90行定义了HtmlHelper类中6个重载的ValidationMessage扩展方法,其中第62行到第90行是ValidationMessage扩展方法中的关键代码。在第74行中,htmlHelper. ViewData.ModelState是一个ModelStateDictionary类的实例化对象,通过指定其中的键“modelName”,得到ModelState类的实例化对象modelState;第76行从modelState对象的Errors属性中获得ModelErrorCollection类的实例化对象modelErrors;第77行从modelErrors对象中得到ModelError类的实例化对象modelError。ModelStateDictionary类、ModelState类、ModelErrorCollection类及ModelError类之间的UML类图如图3-13所示。
图3-13 模型异常信息各类的UML类图
第92行到第151行定义了HtmlHelper类中4个重载的ValidationSummary扩展方法,其中第107行到第151行是ValidationSummary扩展方法中的关键代码。
这里需要特别说明的是,在第137行中,在输入ModelState类型的参数中,传入的参数竟然是一个null值,而不是应有的modelState值,因此对于图3-12中的“UnitPrice”文本框中的出错信息没有显示在ValidationSummary的异常摘要信息中,修改第137行中的传入参数为modelState,即可修正错误。
3.9 思考与提高
本章主要解析了HtmlHelper控件中的7个大类,它们分别是FormExtensions类中的控件BeginForm、BeginRouteForm和EndForm; InputExtensions类中的控件CheckBox、Hidden、Password、RadioButton和TextBox; LinkExtensions类中的控件ActionLink和RouteLink;RenderPartialExtensions类中的控件RenderPartial; SelectExtensions类中的控件DropDownList和ListBox; TextAreaExtensions类中的控件TextArea及ValidationExtensions类中的控件ValidationMessage和ValidationSummary。
请读者在相关的ASP.NET 3.5 MVC项目中熟练使用这些控件,如果这些控件不能满足自己需求,请读者思考如何开发自己所需要的个性化控件。