咱們平時編程寫一些輔助類的時候習慣用「XxxHelper」來命名。一樣,在 MVC 中用於生成 Html 元素的輔助類是 System.Web.Mvc 命名空間下的 HtmlHelper,習慣上咱們把 HtmlHelper 中的(擴展)方法叫 HtmlHelper Method,因爲使用頻繁,就簡單稱爲Helper Method。它的做用是把生成 Html 代碼的任務交給 MVC,以便 MVC 能完成不少自動處理的工做,也減小了代碼量。咱們在 View 中使用的 Html.ActionLink、Html.BeginForm、Html.CheckBox、Html.Raw 方法等都是 HtmlHelper 中的(擴展)方法。本文將進行簡要系統的介紹 Helper Method。html
本文目錄編程
經過自定義 Helper Method,咱們能夠把一大段的 Html 代碼打包成一個方法以便在整個應用程序中重複調用。以下面這個 View:瀏覽器
@{ ViewBag.Title = "Index"; } @helper StripHtml(string input) { @System.Text.RegularExpressions.Regex.Replace(input, "<.*?>", string.Empty) } <div> HTML Content: @HttpUtility.HtmlDecode("<div><p>Test</p><br/></div>") <br /> Plain Content:@StripHtml("<div><p>Test</p></div>") </div>
在 View 中經過 @helper 標記定義的方法,MVC 會把它編譯成 HtmlHelper 類的擴展方法。但在 View 中定義的 Helper Method 的做用域是當前的 View。若是要在整個應用程序都能使用,咱們能夠把它定義在一個公用的類中,以下面的 CustomHelpers 類中定義的 StripHtml 方法和 View 中的是同樣的:框架
public static class CustomHelpers { public static MvcHtmlString StripHtml(this HtmlHelper html, string input) { return new MvcHtmlString(System.Text.RegularExpressions.Regex.Replace(input, "<.*?>", string.Empty)); } }
關於擴展方法,不清楚的讀者能夠閱讀本系列的 [ASP.NET MVC 小牛之路]02 - C#知識點提要 文章。post
上面兩種方式運行效果以下:網站
MVC 框架會自動把從 Controller 中傳遞到 View 的字符串進行Html編碼,以下面Action方法中的字符串:ui
public ActionResult Index() { string message = "This is an HTML element: <input>"; return View((object)message); }
當這個字符串用 Razor 呈現到 View時,生成的 Html 代碼以下: this
This is an HTML element: <input>
Razor 引擎會自動對後臺傳遞過來的字符串進行Html編碼,這是一種保護機制,使得後臺傳遞給View的字符串不與 Html 標記衝突,也避免了用標記語言來攻擊網站的惡意行爲。編碼
但在 Helper Mothod 返回 MvcHtmlString 是被 Razor 信任的,Razor 不會對其進行編碼。咱們能夠簡單驗證一下。url
在 CustomHelpers 類中加入一個 Helper Mothod,以下:
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 View 中以兩種方式來輸出 <input>:
@using MvcApplication1.Infrastructure @model string @{ ViewBag.Title = "Index"; } <p>This is the content from 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>
運行後咱們能夠看到以下兩種結果:
有時候咱們就是想經過Help Meothod 輸出 <input> 字符串怎麼辦,很簡單,像下面這樣把 Helper Method 的返回類型改成 string 類型:
public static string DisplayMessage(this HtmlHelper html, string msg) { return String.Format("This is the message: <p>{0}</p>", msg); }
但它也會把 <p> 給編碼了(以下面左圖),即返回給 View 的字符串會所有被編碼。咱們須要的是把那些要輸出爲 Html 代碼的部分字符串進行編碼,而不是所有,這時咱們能夠這樣作:
public static MvcHtmlString DisplayMessage(this HtmlHelper html, string msg) { string result = String.Format("This is the message: <p>{0}</p>", html.Encode(msg)); return new MvcHtmlString(result); }
此時運行結果以下面右圖:
這部分的內容其實沒什麼好講的,無非就是一些生成表單元素(如 <input type="text"...等)的 Helper Method,經過VS的智能提示能夠很方便的知道每一個方法的用法,本節只對這些 Helper Method 進行一個簡要的歸納。
首先來看看生成 form 元素的 Helper Method。在 View 中經過 Html.BeginForm 和 Html.EndForm 兩個方法能夠很便捷地生成一個 form,以下:
@Html.BeginForm() <label>PersonId</label> <input name="personId" value="@Model.PersonId"/> ... <input type="submit" value="Submit" /> @{Html.EndForm();}
咱們熟悉的另一種寫法能夠把 Html.EndForm 省略,以下:
@Html.BeginForm(){ <label>PersonId</label> <input name="personId" value="@Model.PersonId"/> ... <input type="submit" value="Submit" /> }
BeginForm 有不少重載方法,在須要的時候咱們能夠經過VS智能提示進行了解。
通常放在form表單內的元素的Helper Method就不少了,下面是生成 <input> 標籤的 Helper Method 列表:
這裏列出的每一個 Helper Method 的第一個參數對應 input 元素 的 name 屬性(默認也是id屬性),第二個參數指定了 input 元素的值。它們都有若干個重載方法,這裏列出來的是其中的一個。
這個列表的每一個方法都有帶一個 string 類型參數的重載方法,如 Html.TextBox("DataValue")。對於這個重載方法,MVC 框架會依次從 ViewBag 和 @Model 中去查找值。如 Html.TextBox("DataValue") 方法,MVC 會依次查找 ViewBag.DataValue 和 @Model.DataValue 的值做爲生成 input 的 value 值。咱們能夠驗證一下,以下面的 View
@{ ViewBag.Title = "Index"; ViewBag.DataValue = "This is the value from ViewBag."; } @Html.TextBox("DataValue")
它生成 input 標籤的 value 值以下:
<input id="DataValue" name="DataValue" type="text"value="This is the value from ViewBag." />
若是指定的字符串參數是相似於這種的:DataValue.First.Name ,MVC 會依次查找 ViewBag.DataValue.First.Name、ViewBag.DataValue["First"].Name 等的值,咱們知道有這麼回事就能夠了,不去研究它。
對於上面列表中的 Helper Method 都有對應的強類型版本,如 Html.CheckBox 方法對應有 Html.CheckBoxFor 方法。它們的參數是 lambda 表達式,如:
Html.TextBoxFor(x => x.FirstName)
其中 x 的類型是 View Model 的類型,它會根據 model 的屬性名和屬性值生成input標籤的 name (id 默認和 name 同樣)和 value 屬性值,以下:
<input id="FirstName" name="FirstName" type="text" value="" />
另外,還有一種生成 Select 標籤的 Helper Method,以下面列表所示:
MVC 提供了另一套生成 Html 元素的 Helper Method,它能夠經過 Model 信息來生成想要的 Html 標籤。例如要生成一個文本框能夠用 Html.Editor 方法,它是多行仍是單行或者其餘,能夠在 View Model 中進行定製。它就像一個模板(Template),要怎麼顯示元素,大部分都在 Model 中指定。下面來作個Demo 可能會讓你更地好理解。
爲了演示,咱們先建立一個 Person Model,代碼以下:
namespace MvcApplication1.Models { public class Person { public int PersonId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Role Role { get; set; } } public enum Role { Admin, User, Guest } }
添加一個 Action,以下:
public ActionResult CreatePerson() { return View(new Person()); }
爲該 action 添加 View:
@model MvcApplication1.Models.Person @{ ViewBag.Title = "CreatePerson"; } @Html.Editor("PersonId")
運行後分別在 IE 11(左)和 Chrome(右)瀏覽器中的顯示以下:
若是瀏覽器足夠支持 Html 5 的話,對於數字類型會出現像 Chrome 瀏覽器那樣的上下增減少按鈕。
咱們再來看它生成的 Html 代碼:
<input class="text-box single-line" data-val="true" data-val-number="字段 PersonId 必須是一個數字。" data-val-required="PersonId 字段是必需的。"
id="PersonId" name="PersonId"type="number" value="0" />
這就是模板化的 Helper Method 生成的 Html 代碼,經過生成的這些屬性,MVC 能夠(配合 jQuery 庫)自動完成一些例如客戶端驗證之類的工做(後續博文介紹)。若是須要禁用客戶端驗證,能夠在 View 中添加以下代碼:
@{ Html.EnableClientValidation(false); }
模板化的 Helper Method 有下面幾個:
每一個模板化Helper Method都有兩個版本,它們除了參數不同,其它沒什麼區別。
咱們能夠用C#特性來表示的Model元數據(Metadata)來告訴Helper Method如何爲Model呈現Html元素。如:
public class Person { [HiddenInput(DisplayValue=false)] public int PersonId { get; set; } ... }
當在View中經過 @Html.EditorForModel 或 @Html.EditorFor(m => m.PersonId) 方法時,會生成一個隱藏的 input 元素,以下:
<input id="PersonId" name="PersonId"type="hidden" value="0" />
像這樣「指導」 Helper Method 生成 Html 元素的特性還有不少,如 [Display(Name="Your name")]、[DataType(DataType.Date)]、[UIHint("MultilineText")]等。
前面咱們簡要介紹了 Helper Method 模板根據 Model 元數據生成 Html 元素的便捷之處。但有時候MVC提供的模板並不能知足咱們的需求,這時咱們能夠爲 Model 對象的某個屬性自定義一個 Helper Method 模板。
在前文中咱們知道,使用 Html.DropDownList(For) 能夠爲咱們建立一個下拉列表,但這個方法有一點很差使,每次使用都須要給它構造器的第二個參數指定數據源,非常不方便。對於頻繁使用的下拉列表,咱們能夠把它作成模板。下面我將經過爲 Role 枚舉類型(前文有定義)作一個下拉列表模板來演示如何自定義。
按照約定,MVC框架會在 /Views/Shared/EditorTemplates 文件夾下查找自定義的模板。所以咱們須要先添加一個EditorTemplates 文件夾,而後在該文件夾下添加一個 Role.cshtml 文件,代碼以下:
@model MvcApplication1.Models.Role @Html.DropDownListFor(m => m, new SelectList(Enum.GetNames(Model.GetType()), Model.ToString()))
這樣就作好了一個模板,咱們能夠像下面這樣在一個View中使用它:
@model MvcApplication1.Models.Person
@Html.EditorFor(m => m.Role)
除了 EditorFor,調用任何一個模板化的Helper Method 爲 Role 類型的屬性呈現元素時都會顯示爲一個下拉列表,效果以下:
爲了讓這個功能更通用,使全部枚舉類型均可以顯示爲這樣的下拉列表,咱們再改造一下這個功能。
刪除原來的 Role.cshtml 文件,在同一目錄下再新建一個 Enum.cshtml 分部視圖,代碼參考以下:
@model Enum @Html.DropDownListFor(m => m, Enum.GetValues(Model.GetType()).Cast<Enum>() .Select(m => { string enumVal = Enum.GetName(Model.GetType(), m); return new SelectListItem() { Selected = (Model.ToString() == enumVal), Text = enumVal, Value = enumVal }; }) )
而後咱們能夠在 Model 中經過 UIHint 特性來應用它,以下:
public class Person { public int PersonId { get; set; } ... [UIHint("Enum")] public Role Role { get; set; } }
再運行程序看到的效果是和上面同樣的。
注意,MVC 是根據屬性的類型在 /Views/Shared/EditorTemplates 目錄下找自定義的模板的,因此必定要保證模板的文件名和屬性類型名一致(或用UIHint特性指定爲模板的名稱)。
另外,若是自定義的模板和內置的模板同名,MVC會使用自定義的。能夠根據這個特色來用自定義的模板替換系統內置的。例如,若是在 EditorTemplates 文件夾下建立一個 Boolean.cshtml,當MVC要爲 Boolean 類型的屬性呈現 Html 元素時,它會使用自定義的 Boolean.cshtml 分部視圖來呈現。
參考:《Pro ASP.NET MVC 4 4th Edition》