Asp.Net MVC4系列--进阶篇之Helper(1)

从本章开始,将为读者介绍MVC4中的helper使用方法

 

从sample开始

 

准备controller和Action

 

   public class TestController : Controller
    {
        public ActionResult Index()
        {
           ViewBag.Fruits = new[] { "Apple", "Orange","Pear" };
           ViewBag.Cities = new[] { "New York", "London","Paris" };
            string message = "This is an HTML element: <input>";
            return View("List",(object)message);
 
        }
    }


 

代码说明:准备了一个TestController,包含了一个Index Action(默认Action),Action中存了一些stringArray在ViewBag,返回了一个字符串(包含了htmltag)。

 

准备View

 

<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<div>
Here are the fruits:
@foreach (string str in (string[])ViewBag.Fruits) {
<b>@str </b>
}
</div>
<div>
Here are the cities:
@foreach (string str in (string[])ViewBag.Cities) {
<b>@str </b>
}
</div>
<div>
Here is the message:
<p>@Model</p>
</div>
</body>
</html>


 

代码说明:把Viewbag中的两个string Array打印出来,并打印出传来的message。

 

 

测试:

 

Asp.Net MVC4系列--进阶篇之Helper(1)

可以看到,View打印出了string array,并且htmltag被识别为了string。

 

下一步,创建一个inline的helper

现在希望对打印字符串数组的View中的代码做一下封装,这样可以很好的控制打印的这部分实现。

于是给出实现:

@helper  ListArrayItems(string[] items) {
foreach(string str in items) {
<b>@str </b>
}
}


 

在打印时候,就像调用C#函数那样调用就可以了,传入ViewBag中的Array:

<div>
Here are the fruits:
@ListArrayItems(ViewBag.Fruits)
</div>
<div>
Here are the cities:
@ListArrayItems(ViewBag.Cities)
</div>


使用helper的好处显而易见,其实就是封装带来的好处,我们可以集中对行为进行控制,例如希望显示为列表,只需要给helper改为:

@helper  ListArrayItems(string[] items) {
<ul>
@foreach(string str in items) {
<li>@str</li>
}
</ul>
}


 

测试:

Asp.Net MVC4系列--进阶篇之Helper(1)

 

实现ExternalHelper method

不想把代码coding在View里,希望写在C#里来管理,那么可以考虑实现一个External版本的:

第一步,实现一个customizehelper

 

Public  static class MyHelpers
    {
        public staticMvcHtmlString ListArrayItems(this HtmlHelper html, string[] list)
        {
            var  tag =new  TagBuilder("ul");
            foreach(var str in list)
            {
                var itemTag = new  TagBuilder("li");
               itemTag.SetInnerText(str);
               tag.InnerHtml += itemTag.ToString();
            }
            return  new MvcHtmlString(tag.ToString());
        }
}


 

可以看到,实际上是对HtmlHelper的扩展,接收一个stringarray参数,打印为<ul></li>列表,使用的是TagBuilder,返回值是MvcHtmlString。

1.      在View中comment掉以前的inline版本,应用external版本的实现:

@*@helper ListArrayItems(string[] items) {
<ul>
@foreach(string str in items) {
<li>@str</li>
}
</ul>
} *@
@using MVCHelperStudy.CustomizeHelper


 

2.      调用

<div>
Here are the fruits:
@Html.ListArrayItems((string[])ViewBag.Fruits)
</div>
<div>
Here are the cities:
@Html.ListArrayItems((string[])ViewBag.Cities)
</div>


 

HtmlHelper中常用的property:

 

RouteCollection

Application定义的路由

ViewBag

从调用的Controller/Action传进来的ViewBag

ViewContext

包含了请求信息,Controller,View等等

 

ViewContext中常用的property

 

 

 

 

Controller

当前请求调用的Controller

HelperContext

包含了当前请求的详细信息(Request,Reponse等等)

IsChildAction

调用是否来自ChildAction

RouteData

当前请求的路由信息

View

调用当前helper的view

 

TagBuilder的常用函数,property

InnerHtml

设置控件内部html

SetInnerText(string)

设置控件包含的text

AddCssClass(string)

设置控件的css class

MergeAttribute(string,string,bool)

设置attribute,指定是否覆盖旧值

 

 

Partial View , Child Action , Helper Method

前面章节讲过了PartialView,ChildAction,这次也演示了Helper的用法,那么什么时候用哪个?

一般的,Helper会对Html的封装多一些,如果希望封装部分html的展示方法,可以考虑helper,一般会使用inline形式的,因为个人认为mvc后端写external版本的helper比较蹩脚。

 

如果对于场景:来了一个请求,找到了action,action希望停留在当前view,完成一个局部刷新(ajaxcall),那么可考虑partialview最合适了,这种场景,只涉及到action接到请求,来选择view。

 

如果再复杂一些,view中一定要callaction,操作一下model,反过来render一个view,那么就考虑采用childaction。不过一般情况下,接收请求返回partialview足以cover大部分需要应用ajax的场景,如果不够,对于比较特殊的页面,例如需要SPA(singlepage application ) , 那么js template + JsonResult也是个不错的方案。

 

字符串Encoding

对于上例,Action中返回的字符串中包含了”<input>”,如果希望mvc自动识别为一个输入框,如何做?

 

准备一个helper

 

        public static  MvcHtmlStringDisplayMessage(this HtmlHelper html, string msg)
        {
            string result =String.Format("This is the message: <p>{0}</p>", msg);
            return new  MvcHtmlString(result);
        }


与直接显示出model(字符串类型)不同,我们包了一层,构造一个MvcHtmlString对象丢给mvcframework,这样它就会识别为一个html输出到浏览器。

改一下view

把刚才的View代码删了,添加以下代码:

 

@model string
@using MVCHelperStudy.CustomizeHelper
 
<html>
<head>
<meta name="viewport" content="width=device-width"/>
<title>Index</title>
</head>
<body>
    <p>This is the contentfrom the view:</p>
<div style="border: thin solid black; padding: 10px">
Here is the message:
<p>@Model</p>
</div>
<p>This is the content from the helper method:</p>
<div style="border: thin solid black; padding: 10px">
@Html.DisplayMessage(Model)
</div>


 

代码说明:我们打印了两个字符串,一个是直接取@Model的,一个是调用了新的helper(Wrap为MvcHtmlString对象)

测试

Asp.Net MVC4系列--进阶篇之Helper(1)

可以看到我们的html字符串被mvcframework识别为了控件render在浏览器上

 

现在改一下,如果我们的目的就是希望mvcframework把我的html字符串识别为字符串,那么helper怎么写,简单的返回字符串就好了:

 

Public  static string DisplayMessage(this HtmlHelper html, string msg) {
return String.Format("This is the message:<p>{0}</p>", msg);
}


 

第二种方法

使用Html.Encode,依然返回MvcHtmlString:



Public  static MvcHtmlString DisplayEncodeMessage(this HtmlHelper html, string msg) { 
String  encodedMessage = html.Encode(msg); 
string result = String.Format("This is the message: <p>{0}</p>", encodedMessage); 
return new  MvcHtmlString(result); 
}



View调用一下:

Remove掉所有代码添加这行,   @Html.DisplayEncodeMessage(@Model)

 

测试:

Asp.Net MVC4系列--进阶篇之Helper(1)

 

使用自带的helper来构造一个表单

Mvc framework给我们提供了很多自带的生成html的方法,让我们可以生成表单。

从一个例子开始,

在Model文件夹添加Person类

public class Person
    {
        public int PersonId { get;set; }
        public string FirstName {get; set; }
        public string LastName {get; set; }
        public DateTime BirthDate {get; set; }
        public Address HomeAddress{ get; set; }
        public bool IsApproved {get; set; }
        public Role Role { get;set; }
    }
    public class Address
    {
        public string Line1 { get;set; }
        public string Line2 { get;set; }
        public string City { get;set; }
        public string PostalCode {get; set; }
        public string Country {get; set; }
    }
    public enum Role
    {
        Admin,
        User,
        Guest
}


 

一共三个类:person,address,role。

 

准备一个Controller,代码

 

public  class PersonController : Controller
    {
        public ActionResult CreatePerson()
        {
           return  View(new Person());
        }
 
       [HttpPost]
        public ActionResult  CreatePerson(Person person)
        {
           return  View(person);
        }
    }


代码说明:

准备两个action,一个是不带数据的,主要针对第一次来到这个页面的情况;一个是接收数据的,针对提交表单的情况,并使用了httppost对请求访问进行限制。

 

View的实现

<html>
@model MVCHelperStudy.Models.Person
@{
ViewBag.Title = "CreatePerson";
}
<h2>CreatePerson</h2>
<form action="/Person/CreatePerson" method="post">
<div class="dataElem">
<label>PersonId</label>
<input  name="personId"  value="@Model.PersonId"/>
</div>
<div  class="dataElem">
<label>First Name</label>
<input  name="FirstName" value="@Model.FirstName"/>
</div>
<div  class="dataElem">
<label>Last Name</label>
<input  name="lastName" value="@Model.LastName"/>
</div>
<input  type="submit" value="Submit" />
</form>


代码说明:

在不使用任何html helper 函数的情况下,手动coding一个form,表单指向Person/CreatePerson,method为post。


测试

1.      第一次访问这个页面,可以看到View拿到了一个空的Person对象

 

Asp.Net MVC4系列--进阶篇之Helper(1)

 

2.      Fill这个form:

Asp.Net MVC4系列--进阶篇之Helper(1)

3.      提交表单

笔者在Action设置了断点,为了说明请求成功被路由到了post版本的CreatePersonAction中。

 

Asp.Net MVC4系列--进阶篇之Helper(1)


注意,我们手动为每一个input设置了name属性,这个很重要,因为mvcFramework在提交表单时会拿input的name属性和model进行匹配并赋值,如果把name拿掉会怎样?提交表单时这个input的值就没法带到action了。例如拿掉lastName的name属性:

   <input  value="@Model.LastName"/>


然后按照刚才的流程提交表单,

Asp.Net MVC4系列--进阶篇之Helper(1)

在调试状态下查看LastName的值:

Asp.Net MVC4系列--进阶篇之Helper(1)

可以看到,FirstName和Id的数值都被带过来了,但是LastName由于name属性没有设置导致mvcframework没有办法匹配,因此数据没有被正确赋值导致binding失败,关于modelbinding,后面章节会讲到。

 

使用Html.BeginForm, Html.EndForm 生成表单控件

 

顾名思义,这个函数就是生成<form>tag的,下面的列表是几种重载和说明:

BeginForm()

指向当前的controller和当前view对应的action, http method :Get

BeginForm(action,controller)

指定一个controller/action

http method : Get

BeginForm(action,controller,method)

指定controller/action和

Http method

BeginForm(action,controller,method,attributes)

指定controller/action 和

http method ,并设置form的一些属性(例如 class)

BeginForm(action,controller,routeValues,method,attributes)

指定controller/action,设置method,传route的匿名对象,并可以设置form attribute

我们可以看到,从第一个到最后一个重载,我们可以自定义的事情越来越多,现在以一个最复杂的最后一个为例,看看生成的form是怎样的,这样我们可以考虑不同场景来决定是否使用这个方法:

 

@using (Html.BeginForm("CreatePerson", "Person",
new { id = "MyIdValue" }, FormMethod.Post,
new { @class = "personClass",data_formType="person"})) {
////html
}


生成的form

<form action="/Person/CreatePerson/MyIdValue" class="personClass" data-formType="person"
method="post">


 

生成form时,specify一个路由

 

如果有需要,告诉mvcframework在生成表单时,按照指定的路由配置来生成,也是可以做到的:

对于路由配置:

            routes.MapRoute(
name: "FormRoute",
url: "app/forms/{controller}/{action}"
);


 

编写View代码

   @using(Html.BeginRouteForm("FormRoute", new {},FormMethod.Post,
new { @class = "personClass",data_formType="person"})) {
//html
}


 

 

生成的表单html(Form部分)

<form  action="/app/forms/Person/CreatePerson" class="personClass"  data-formType="person"method="post"><div class="dataElem">

 

这样就完成了手动根据路由来生成form的实现。


使用html helper

常用的用于html helper生成控件

CheckBox

Html.CheckBox(“name”,false)

<input id="myCheckbox" name="myCheckbox" type="checkbox" value="true" />

 

<input name="myCheckbox" type="hidden" value="false" />

Hidden Field

Html.Hidden("myHidden", "val")

<input id="myHidden" name="myHidden" type="hidden" value="val" />

Radio Button

Html.RadioButton("myRadiobutton", "val", true)

<input checked="checked" id="myRadiobutton" name="myRadiobutton"

type="radio" value="val" />

Password

Html.Password("myPassword", "val")

<input id="myPassword" name="myPassword" type="password" value="val" />

Text Area

Html.TextArea("myTextarea", "val", 5, 20, null)

<textarea cols="20" id="myTextarea" name="myTextarea" rows="5">

val</textarea>

Text box

Html.TextBox("myTextbox", "val")

<input id="myTextbox" name="myTextbox" type="text" value="val" />

 

使用html helper重构刚才的View

 

@using(Html.BeginRouteForm("FormRoute",new {}, FormMethod.Post,
new { @class ="personClass", data_formType="person"})) {
<div class="dataElem">
<label>PersonId</label>
@Html.TextBox("personId",@Model.PersonId)
</div>
<div class="dataElem">
<label>FirstName</label>
@Html.TextBox("firstName",@Model.FirstName)
</div>
<div class="dataElem">
<label>LastName</label>
@Html.TextBox("lastName",@Model.LastName)
</div>
<input type="submit" value="Submit" />
}


 

运行,查看浏览器生成的html:

<form action="/app/forms/Person/CreatePerson" class="personClass" data-formType="person" method="post"><div class="dataElem">
<label>PersonId</label>
<input data-val="true" data-val-number="The field PersonId must be anumber." data-val-required="The PersonId field is required." id="personId" name="personId" type="text"value="0" />
</div>
<div class="dataElem">
<label>FirstName</label>
<input id="firstName" name="firstName" type="text"value="" />
</div>
<div class="dataElem">
<label>LastName</label>
<input id="lastName"  name="lastName"  type="text" value="" />
</div>
<input type="submit"  value="Submit" />
</form>


 

结果:

1.      form标签按照我们希望的方式生成了(根据路由配置生成)

2.      htmlhelper生成了input,每个都有name,名称为我们传入的那个名字。

关于Html.TextBox(“”),mvcframework会分别从ViewData, ViewBag,和@Model.DataValue中查找指定的property名称,找到第一个就直接返回,完成匹配。


强类型的form表单

如果使用Html.TextBox(“name”)传字符串不太满意,希望传一个Member表达式,可以使用Html.TextBoxFor来达到目的:

支持强类型生成html控件的常用方法

CheckBox

Html.CheckBoxFor(x => x.IsApproved)

<input id="IsApproved" name="IsApproved" type="checkbox" value="true" />

<input name="IsApproved" type="hidden" value="false" />

HiddenField

Html.HiddenFor(x => x.FirstName)

<input id="FirstName" name="FirstName" type="hidden"  value="" />

RadioButton

Html.RadioButtonFor(x => x.IsApproved, "val")

<input id="IsApproved" name="IsApproved" type="radio" value="val" />

Password

Html.PasswordFor(x => x.Password)

<input id="Password" name="Password" type="password" />

Text Area

Html.TextAreaFor(x => x.Bio, 5, 20, new{})

<textarea cols="20" id="Bio" name="Bio" rows="5">Bio value</textarea>

Text Box

Html.TextBoxFor(x => x.FirstName)

<input  id="FirstName" name="FirstName" type="text" value="" />

 

几乎和字符串版本的类似,区别就是签名多了一个For,接收的参数为一个MemberExpression。

 

使用强类型的版本重构我们的View:

@using(Html.BeginRouteForm("FormRoute",new {}, FormMethod.Post,
new { @class = "personClass",data_formType="person"})) {
<div class="dataElem">
<label>PersonId</label>
@Html.TextBoxFor(m => m.PersonId)
</div>
<div class="dataElem">
<label>First Name</label>
@Html.TextBoxFor(m => m.FirstName)
</div>
<div class="dataElem">
<label>Last Name</label>
@Html.TextBoxFor(m => m.LastName)
</div>
<input  type="submit"value="Submit" />
}


验证一下浏览器生成的html是我们希望的:

 

<form action="/app/forms/Person/CreatePerson" class="personClass" data-formType="person" method="post"><div class="dataElem">
<label>PersonId</label>
<input  data-val="true" data-val-number="The field PersonId must be a number." data-val-required="The PersonId field is required." id="PersonId" name="PersonId" type="text" value="0" />

<div class="dataElem">
<label>First Name</label>
<input id="FirstName" name="FirstName" type="text" value="" />
</div>
<div class="dataElem">
<label>Last Name</label>
<input id="LastName" name="LastName" type="text" value="" />
</div>
<input type="submit" value="Submit" />
</form>


 

可以看到,mvcframework为我们生成了希望的html。

DropDownList

前面的例子没有提到Dropdown,是希望以其为特例演示一下强类型的显示。

1.      先看一下html自带的关于drop-down生成的方法

Html.DropDownList("myList", new SelectList(new [] {"A", "B"}), "Choose")

<select id="myList" name="myList">

<option value="">Choose</option>

<option>A</option>

<option>B</option>

</select>

Html.DropDownListFor(x => x.Gender, new SelectList(new [] {"M", "F"}))

<select id="Gender" name="Gender">

<option>M</option>

<option>F</option>

</select>

Html.ListBox("myList", new MultiSelectList(new [] {"A", "B"}))

<select id="myList" multiple="multiple" name="myList">

<option>A</option>

<option>B</option>

</select>

Html.ListBoxFor(x => x.Vals, new MultiSelectList(new [] {"A", "B"}))

<select id="Vals" multiple="multiple" name="Vals">

<option>A</option>

<option>B</option>

</select>

 

可以看到,mvc framework提供了不同的重载,我们可以生成multiselect和select。

生成带有role的表单,修改View代码:

   @using(Html.BeginRouteForm("FormRoute", new {},FormMethod.Post,
new {@class = "personClass", data_formType="person"})) {
<div class="dataElem">
<label>PersonId</label>
@Html.TextBoxFor(m=> m.PersonId)
</div>
<div class="dataElem">
<label>FirstName</label>
@Html.TextBoxFor(m=> m.FirstName)
</div>
<div class="dataElem">
<label>LastName</label>
@Html.TextBoxFor(m=> m.LastName)
</div>
<div class="dataElem">
<label>Role</label>
@Html.DropDownListFor(m=> m.Role,
new SelectList(Enum.GetNames(typeof(MVCHelperStudy.Models.Role))))
</div>
<input  type="submit"value="Submit" />
}


这次我们添加了Role,我们使用了Enum的静态方法获得指定Enum类型中的所有值。传递给mvcframework的是Member Expression和string array。

 

测试:

Asp.Net MVC4系列--进阶篇之Helper(1)

浏览器生成的select标签

<div class="dataElem">
<label>Role</label>
<select  data-val="true"  data-val-required="The Role field isrequired."  id="Role" name="Role">
<option selected="selected">Admin</option>
<option>User</option>
<option>Guest</option>
</select>
</div>


 

 

Asp.Net MVC4系列--进阶篇之Helper(1),布布扣,bubuko.com

Asp.Net MVC4系列--进阶篇之Helper(1)

上一篇:网易新闻页面信息抓取 -- htmlagilitypack搭配scrapysharp


下一篇:node.js第二课(入门)