第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所示。

active=true

图3-1 HtmlHelper控件中的各个类

HtmlHelper类实现了ASP.NET 3.5 MVC项目中的各种控件,其内部是通过各种扩展方法来实现的,可以满足一般项目中视图的开发。

开发者根据自己的需求,可以通过扩展方法开发自己所需要的控件,在第4章中,就实现了一个功能较为完善的数据显示GridView控件。

3.1.2 视图中的HTML属性

为方便开发者使用HtmlHelper控件,在视图ViewPage类中(该类的UML类图,如图3-2所示),专门设置了一个HTML属性,代码如下:

active=true

图3-2 ViewPage类的UML类图

        public HtmlHelper Html { get; set; }

从上述代码中可以看出,HTML属性就是HtmlHelper的类型,因此在视图页面中通过HTML属性调用实现控件的相关扩展方法。

3.2 FormExtensions类

FormExtensions类的UML类图,如图3-3所示。

active=true

图3-3 FormExtensions的UML类图

FormExtensions类是一个静态类,其中定义了3种类型的扩展方法,供开发者在视图中设置表单和表单路由的定义,它们分别是BeginForm、BeginRouteForm、EndForm,以下分别予以说明。

3.2.1 BeginForm

BeginForm扩展方法,主要实现表单定义的开始部分,定义了13个重载的扩展方法供开发者使用,见表3-1。

表3-1 BeginForm()的13个重载方法

active=true

对于第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个重载方法

active=true

对于第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所示。

active=true

图3-4 InputExtensions的UML类图

InputExtensions类中定义了5种类型的扩展方法,供开发者在视图中设置CheckBox控件、Hidden控件、Password控件、RadioButton控件及TextBox控件,以下分别予以说明。

3.3.1 CheckBox

CheckBox扩展方法,主要实现复选框控件,定义了6个重载的扩展方法供开发者使用,见表3-3。

表3-3 CheckBox()的6个重载方法

active=true

对于第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个重载方法

active=true

对于第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个重载方法

active=true

对于第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个重载方法

active=true

对于第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个重载方法

active=true

对于第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所示。

active=true

图3-5 LinkExtensions的UML类图

LinkExtensions类中定义了两种类型的扩展方法,供开发者在视图中设置相关的链接,它们分别是ActionLink和RouteLink,以下分别予以说明。

3.4.1 ActionLink

ActionLink扩展方法主要实现一个链接,定义了10个重载的扩展方法供开发者使用,见表3-8。

表3-8 ActionLink()的10个重载方法

active=true

对于第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个重载方法

active=true

对于第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所示。

active=true

图3-6 RenderPartialExtensions的UML类图

RenderPartialExtensions类是一个静态类,其中定义了4个重载的扩展方法RenderPartial,以便开发者在视图中显示相关的用户控件,见表3-10。

表3-10 RenderPartial()的4个重载方法

active=true

对于第1个重载方法,可以设置如下代码:

        <% Html.RenderPartial("List" ); %>

在上述代码中,需要被显示的用户控件为List.ascx,该用户控件主要用来显示数据表Categories中的数据。

上述代码的运行界面如图3-7所示。

active=true

图3-7 显示用户控件的运行界面

在ASP.NET 3.5 MVC项目中,通过使用RenderPartial扩展方法来显示用户控件,便于视图页面的封装。

3.6 SelectExtensions类

SelectExtensions类的UML类图,如图3-8所示。

active=true

图3-8 SelectExtensions的UML类图

SelectExtensions类是一个静态类,其中设置了3种类型的扩展方法,以便开发者在视图中设置相关的界面控件,它们分别是DropDownList控件和ListBox控件。

3.6.1 DropDownList

DropDownList扩展方法主要实现一个下拉列表框,定义了8个重载的扩展方法供开发者使用,见表3-11。

表3-11 DropDownList()的8个重载方法

active=true

对于第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个重载方法

active=true

对于第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所示。

active=true

图3-9 TextAreaExtensions的UML类图

TextAreaExtensions类中定义了8种重载的扩展方法TextArea,以便开发者在视图中设置TextArea控件,见表3-13。

表3-13 TextArea()的8个重载方法

active=true

对于第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所示。

active=true

图3-10 ValidationExtensions的UML类图

ValidationExtensions类是一个静态类,其中定义了两种类型的扩展方法,以便让开发者实现相关控件的输入验证,这些验证控件分别是ValidationMessage控件和ValidationSummary控件。

3.8.1 VaIidationMessage

在ValidationExtensions类中,定义了6种重载的ValidationMessage扩展方法,以便开发者在视图中设置相关控件的输入验证,见表3-14。

表3-14 ValidationMessage()的6个重载方法

active=true

对于第3个重载方法,可以设置如下代码:

          <%= Html.TextBox("ProductName") %>
          <%= Html.ValidationMessage("ProductName", "*") %>

在上述代码中,设置了验证信息控件ValidationMessage的模型名称为“ProductName”,而验证信息则为星号“*”,表明如果文本框中的输入信息为空白,则此时的文本框会变成红色,并在文本框的右边出现一个星号“*”提示信息,说明输入信息有误。

3.8.2 VaIidationSummary

在ValidationExtensions类中,定义了4种重载的ValidationSummary扩展方法,以便开发者在视图中设置相关控件的输入验证摘要,见表3-15。

表3-15 ValidationSummary()的4个重载方法

active=true

设置验证信息控件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所示。

active=true

图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文本框中的出错信息。

active=true

图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所示。

active=true

图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项目中熟练使用这些控件,如果这些控件不能满足自己需求,请读者思考如何开发自己所需要的个性化控件。