天天看點

[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。

本文目錄

自定義 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#知識點提要 文章。

上面兩種方式運作效果如下:

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

字元串編碼問題

MVC 架構會自動把從 Controller 中傳遞到 View 的字元串進行Html編碼,如下面Action方法中的字元串:

public ActionResult Index() {
    string message = "This is an HTML element: <input>";
    return View((object)message);
}      

當這個字元串用 Razor 呈現到 View時,生成的 Html 代碼如下: 

This is an HTML element: &lt;input&gt;
      

Razor 引擎會自動對背景傳遞過來的字元串進行Html編碼,這是一種保護機制,使得背景傳遞給View的字元串不與 Html 标記沖突,也避免了用标記語言來攻擊網站的惡意行為。

但在 Helper Mothod 傳回 MvcHtmlString 是被 Razor 信任的,Razor 不會對其進行編碼。我們可以簡單驗證一下。

在 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>      

運作後我們可以看到如下兩種結果:

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

有時候我們就是想通過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);
}      

此時運作結果如下面右圖:

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

表單元素 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 清單:

[ASP.NET MVC 小牛之路]13 - 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,如下面清單所示:

[ASP.NET MVC 小牛之路]13 - 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(右)浏覽器中的顯示如下:

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

如果浏覽器足夠支援 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 有下面幾個:

[ASP.NET MVC 小牛之路]13 - 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 類型的屬性呈現元素時都會顯示為一個下拉清單,效果如下:

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

為了讓這個功能更通用,使所有枚舉類型都可以顯示為這樣的下拉清單,我們再改造一下這個功能。

删除原來的 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》

作者:精緻碼農-王亮

出處:http://cnblogs.com/willick

聯系:[email protected]

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。如有問題或建議,請多多賜教,非常感謝。

繼續閱讀