辅助器方法(Helper Method),其作用是对代码块和标记进行打包,以便能够在整个MVC框架应用程序中重用。
首先新建一个项目,添加一个Home控制器,如下图所示:
public class HomeController : Controller{// GET: Homepublic ActionResult Index(){ViewBag.Fruits = new string[] { "Apple", "Orange", "Pear" };ViewBag.Cities = new string[] { "New York","London","Paris"};string message = "This is an HTML element:<input>";return View((object)message);}}
对应的视图Index.cshtml页面,代码如下图所示:
@{Layout = null;
}<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width" /><title>Index</title>
</head>
<body><div> Here are the fruites:@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>
运行效果如下图所示:
创建内联的自定义辅助器方法
修改Index.cshtml页面,代码如下图所示:
@{Layout = null;
}@helper ListArrayItems(string [] items) { foreach (string str in items){<b>@str</b>}
}<!DOCTYPE html>
<html>
<head><meta name="viewport" content="width=device-width" /><title>Index</title>
</head>
<body><div> Here are the fruites:@ListArrayItems(ViewBag.Fruits)</div><div>Here are the cities:@ListArrayItems(ViewBag.Cities)</div><div>Here is the message:<p>@Model</p></div>
</body>
</html>
在上述代码中,定义了一个名称为ListArrayItems的辅助器,它以一个字符串数组为参数。 虽然内联辅助器看上去像一个方法。但是它没有返回值。这种办法的好处是,如果希望改变数组内容显示的方式,只需要一处修改,如下图所示:
@helper ListArrayItems(string [] items) {<ul>@foreach (string str in items){<li>@str</li>}</ul>
}
运行效果如下图所示:
创建外部辅助器方法
内联辅助器是方便的,但是只能在声明它们的视图之中使用,而且,如果内联辅助器太复杂,可能会占据视图,影响阅读。这时就可以用到外部辅助器方法。在项目中添加一个Infrastructure文件夹,并创建一个新的类文件CustomHelp.cs,如下图所示:
public static class CustomerHelpers{public static MvcHtmlString ListArrayItems(this HtmlHelper html,string[] list){TagBuilder tag = new TagBuilder("ul");foreach (string str in list){TagBuilder itemTag = new TagBuilder("li");itemTag.SetInnerText(str);tag.InnerHtml += itemTag.ToString();}return new MvcHtmlString(tag.ToString());}}
辅助器方法的第一个参数是HtmlHelper对象,它以this关键字为前缀,告诉C#编译器,这是一个扩展方法。HtmlHelper能够在创建内容时,对有用信息进行访问,如下图所示:
属性 | 描述 |
RouteCollection | 返回应用程序定义的路由集合 |
ViewBag | 返回视图包数据,这些数据是从动作方法传递给调用辅助器方法的视图的 |
ViewContext | 返回ViewContext对象,该对象能够对请求的细节以及请求的处理方式进行访问 |
属性 | 描述 |
Controller | 返回处理当前请求的控制器 |
HttpContext | 返回描述当前请求的HttpContext对象 |
IsChildAction | 如果已调用辅助器的视图正由一个子动作渲染,则返回true |
RouteData | 返回请求的路由数据 |
View | 返回调用辅助器方法的IView实现的实例 |
创建HTML最容易的方式是使用TagBuilder类,它能够建立HTML字符串,而不需要处理转义及特殊字符串。
成员 | 描述 |
InnerHtml | 这是一个以HTML字符串来设置元素内容的属性。赋给这个属性的值将不进行编码,意即可以将它嵌入HTML元素。 |
SetInnerText(string) | 设置HTML元素的文本内容。string参数将被编码,以使它安全显示。 |
AddCssClass(string) | 对HTML元素添加一个CSS的class |
MergeAttribute(string,string,bool) | 对HTML元素添加一个属性,第一个参数是属性名称,第二是它的值,bool参数指定是否替换已存在的同名属性。 |
使用自定义的外部辅助器方法
@model string
@using WebApplication1.Infrastructure
@{Layout = null;
}<!DOCTYPE html>
<html>
<head><meta name="viewport" content="width=device-width" /><title>Index</title>
</head>
<body><div> Here are the fruites:@Html.ListArrayItems((string [])ViewBag.Fruits)</div><div>Here are the cities:@Html.ListArrayItems((string[])ViewBag.Cities)</div><div>Here is the message:<p>@Model</p></div>
</body>
</html>
运行效果还是一样:
辅助器方法中的字符串编码
MVC框架会保护你免收恶意数据的攻击,这是通过对数据进行自动编码实现的。
如Controller中代码是这样的:
View的显示结果是这样的:
而MVC框架会认为辅助器方法生成内容是安全的。辅助器方法会在浏览器端显示input元素,而这个这可能会被利用以破坏程序的行为。在CustomerHelpers定义一个新的辅助方法看看这一情况:
public static MvcHtmlString DisplayMessage(this HtmlHelper html, string msg){string result = string.Format("This is the message:<p>{0}</p>",msg);return new MvcHtmlString(result);}
修改Index.cshtml页面如下所示:
@model string
@using WebApplication1.Infrastructure
@{Layout = null;
}<!DOCTYPE html>
<html>
<head><meta name="viewport" content="width=device-width" /><title>Index</title>
</head>
<body><div> Here are the fruites:@Html.ListArrayItems((string [])ViewBag.Fruits)</div><div>Here are the cities:@Html.ListArrayItems((string[])ViewBag.Cities)</div><p> This is content from the view</p><div style="border:thin solid black; padding:10px"></div><div>Here is the message:<p>@Model</p></div><p> This is content from the helper method</p><div style="border:thin solid black; padding:10px"></div><div>Here is the message:<p>@Html.DisplayMessage(Model)</p></div></body>
</html>
运行效果如下图所示:
对辅助器方法的内容进行编码
有两种方式:第一种最简单的解决办法是将辅助器方法的返回值类型改为string。但是这种方式在需要生成HTML元素时可能会有问题,不需要生成HTML元素时会比较简单,如下图所示:
public static string DisplayMessage(this HtmlHelper html, string msg){string result = string.Format("This is the message:<p>{0}</p>", msg);return result;}
运行情况如下图所示,对p元素也进行了编码:
第二种方式是用HtmlHelp类中定义的Encode方法 ,选择性的对数据进行编码,代码如下图所示:
public static MvcHtmlString DisplayMessage(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);}
运行情况如下图所示:
使用内建的Form辅助器方法
重载 | 描述 |
BeginForm() | 创建一个表单,回递给源动作方法(源动作方法指引发渲染该表单的动作方法) |
BeginForm(action,controller) | 创建一个表达,回递给以字符串形式指定的动作方法和控制器 |
BeginForm(action,controller,method) | 上一个方法的重载版本,但可以用System.Web.Mvc.FormMethod枚举一个值,来指定form元素中的method标签属性的值 |
BeginForm(action,controller,method,attributes) | 上一个方法的重载版本,但能够为form元素的标签属性指定一个对象,该对象的属性作为标签属性的名称 |
BeginFom(action,controller,routeValues,method,attributtes) | 上一个方法的重载版本,但能够为应用程序路由配置中的路由片段变量指定一个值,这个值作为一个对象,该对象的属性对应于路由变量。 |
例如,举例一个最复杂的重载:
@using (Html.BeginForm(
"CreatePerson", //动作方法名称
"Home", //控制器名称
new { id = "MyIdValue"}, //路由配置中id片段变量的值
FormMethod.Post, //form元素中method标签属性的值
new { @class = "personClass",data_formType = "person"} // form元素的其他标签属性
))
指定表单使用的路由
当使用BeginForm方法时,MVC框架会在能够用来生成指定动作和控制器目标的URL的路由配置中找出一条路由。从本质上说,这是让MVC框架去判断进行的路由选择。如果你希望确保使用一条路由,那么可以替换成BeginRouteForm方法。
假设新增如下路由:
routes.MapRoute(name:"FormRoute",url:"app/forms/{controller}{action}"
);
BeginRouteForm使用方法如下:
@using(Html.BeginRouteForm(
"FormRoute", //路由名称
new {}, //用于路由片段变量
FormMethod.Post, //用于form的method标签属性
new { @class = "personClass",data_formType = "person"}//用于form的其他标签属性
))
使用输入辅助器
HTML元素 | 示例 |
Check box (检查框或复选框) | Html.CheckBoxFor(x => x.IsApproved) output: <input id = "IsApproved" name = "IsApproved" type = "checkbox" value = "true"/> <input name = "IsApproved" type = "hidden" value = "false"/> |
Hidden field (隐藏字段) | Html.HiddenFor(x => x.FirstName) output: <input id = "FirstName" name = "FirstName" type = "hidden" value = ""/> |
Radio button (无线按钮或单选按钮) | Html.RadioButtonFor(x => x.IsApproved,"val") output: <input id = "IsApproved" name = "IsApproved" type = "radio" value = "val"/> |
Password (口令字段) | Html.PasswordFor(x => x.Password) output: <input id = "Password" name = "Password" type = "password"/> |
Text area (文本区) | Html.TextAreaFor(x => x.Bio,5,20,new{}) output: <textarea cols = "20" id = "Bio" name = "Bio" rows = "5">Bio value</textarea> |
Text box (文本框) | Html.TextBoxFor( x => x.FirstName) output: <input id = "FirstName" name = "FirstName" type = "text" value = ""/> |
使用Select元素
HTML元素 | 示例 |
Drop-down list (下拉列表) | Html.DropDownList("myList", new SelectList(new [] { "A" ,"B"}), "Choose") output: <select id = "myList" name = "myList"> <option value = ""> choose</option> <option> A </option> <option> B</option> </select> |
Drop-down list (下拉列表) | Html.DropDownListFor( x => x.Gender, new SelectList(new [] { "M" ,"F"}), "Choose") output: <select id = "Gender" name = "Gender"> <option value = ""> choose</option> <option> A </option> <option> B</option> </select> |
Mutiple-select (多项选择) | Html.ListBox( "myList", new MultiSelectList(new [] { "A" ,"B"})) output: <select id = "myList" multiple = "multiple ",name = "myList"> <option value = ""> choose</option> <option> A </option> <option> B</option> </select> |
Mutiple-select (多项选择) | Html.ListBoxFor( x => Vals, new MultiSelectList(new [] { "A" ,"B"})) output: <select id = "Vals" multiple = "multiple ",name = "Vals"> <option value = ""> choose</option> <option> A </option> <option> B</option> </select> |