[ASP.NET MVC 小牛之路]13 - Helper Method

咱們平時編程寫一些輔助類的時候習慣用「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

經過自定義 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: &lt;input&gt;

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);
}

此時運行結果以下面右圖:

  

表單元素 Helper Method

這部分的內容其實沒什麼好講的,無非就是一些生成表單元素(如 <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,以下面列表所示:

模板化的 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 模板

前面咱們簡要介紹了 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

相關文章
相關標籤/搜索