接上篇…現在讓我們開始討論如何創建HtmlHelper擴展方法.
在前面我們說到了創建HtmlText類的方方面面。包括為HtmlText創建的擴展方法.這些擴展方法包括直接被VIEw調用的那些擴展方法。下面代碼展示了HtmlText的幾種不同的構造函數:
public static class HtmlHelperExtensions
{
#region Textbox
public static IVIEwObject NewText(
this HtmlHelper HtmlHelper, string name)
{
return NewText(HtmlHelper, name, null);
}
public static IVIEwObject NewText(
this HtmlHelper HtmlHelper, string name, string labelText)
{
return NewText(HtmlHelper, name, labelText, null);
}
public static IVIEwObject NewText(
this HtmlHelper HtmlHelper, string name, string labelText, object value)
{
return NewText(HtmlHelper, name, labelText, value, null, false, true, null);
}
public static IVIEwObject NewText(
this HtmlHelper HtmlHelper, string name, string labelText, object value,
string validationMessage, bool @readonly, bool createLi, object attributes)
{
IViewObject vIEwObject = new HtmlText(
new VIEwRequestContext(HtmlHelper), name, labelText, value,
validationMessage, @readonly, createLi, attributes);
viewObject.StartVIEw();
return vIEwObject;
}
#endregion
//NOTE: SOME CONTENT OMITTED FROM THIS SNipPET
} NewText方法有四個不同版本的重載,這些重載都屬於System.Web.Mvc.HtmlHelper的擴展方法,只有最後一個方法用於真正的起作用,而其他的方法都是這個方法的封裝以便讓用戶使用起來更簡單.上面的代碼中HtmlText對象通過傳入適當的參數來初始化,而view是通過StartView方法來初始化,在StartVIEw中被調用的HtmlText會返回合適的對象動態的將Html注入View.現在讓我們來看看如何在vIEw中使用這些方法。
前面我們已經創建了在VIEw中可使用的HtmlText對象,現在就可以使用了。在前面我們提到,如果想要創建一個textbox來滿足Ricky的標准,我必須寫如下代碼:
<li>
<label for="FirstName">First name</label>
<%= Html.TextBox("FirstName") %>
<%= Html.ValidationMessage("FirstName", "*") %>
</li> 現在通過使用HtmlHelper,我們可以把代碼簡化如下:
<% Html.NewText("FirstName", "First name"); %> 上面兩種方法所生成的Html是完全相同的,我們實現了前面設定的目標。從今往後就可以使用這個Helper來簡化ASP.Net MVC view的開發了。上面代碼中並沒有用到EndVIEw方法.下面我們來研究一個更復雜一些的Html的構造—radio button,看是如何實現的
使用ASP.Net MVC來創建一組radio button,代碼一般如下:
<li>
<div class="option-group" id="GenderContainer">
<label for="Gender">Gender</label>
<% foreach (SelectListItem item in Model.GenderList)
{ %>
<%= Html.RadioButton(item.Text, item.Value)%>
<span><%= item.Text%></span>
<% } %>
</div>
</li> 上面代碼是從AddContactClass.ASPx vIEw中節選的,所有代碼可以從這篇文章的網站下載,上面代碼中ContactController通過Model.GenderList屬性來集中返回代碼:
public ActionResult AddContactClassic()
{
AddContactModel addModel = InternalGetInitialAddModel();
return VIEw(addModel);
}
PRivate AddContactModel InternalGetInitialAddModel()
{
string maleString = Gender.Male.ToString();
string femaleString = Gender.Female.ToString();
IList<SelectListItem> genderRadioButtons = new List<SelectListItem>()
{
new SelectListItem { Text = maleString, Value = maleString },
new SelectListItem { Text = femaleString, Value = femaleString }
};
AddContactModel model = new AddContactModel { GenderList = genderRadioButtons };
return model;
} 生成的Html效果圖如下:
在上面創建radio button的代碼中有很多掩蓋了元素真實意圖(譯者按:比如說為什麼我們這麼寫HTML,是為了滿足Ricky的標准嗎?)的部分,比如說:外層的div和內層的span是為了label而包裹文本.而如果我們需要一組radio button時只需要聲明一下並指定相關的值那不是更爽嗎?下面我們創建HtmlRadioButtonGroup vIEw helper,它可以滿足我們只聲明並指定相關值就能創建出相應的html,使用HtmlRadioButtonGroup,我們可以將前面的radio button精簡如下:
<% Html.NewRadioButtonGroup("Gender", Model.GenderList); %> 上面代碼中,我們可以從更高的視角來創建Html,清楚的這段代碼的作用而不是關注Html的細節。下面來創建一個替我們生成HTML的helper,也就是為:HtmlRadioButtonGroup類,下面代碼展示了這個類唯一的構造函數和它的字段:
private readonly List<SelectListItem> mSelectList;
private readonly bool mCreateLi;
public HtmlRadioButtonGroup(
VIEwRequestContext requestContext, string name,
IEnumerable<SelectListItem> selectList, bool createLi, object attributes)
: base(requestContext, name)
{
mSelectList = new List<SelectListItem>();
if (selectList != null)
{
mSelectList.AddRange(selectList);
}
mCreateLi = createLi;
Attributes = attributes;
}看上去是不是和我們先前的HtmlText對象的構造器很像?它的構造函數為通過傳參的方式將RequestContext變得可用。並且通過構造函數為所有的字段進行初始化,這也意味著這個類是在StartView方法中(譯者按:因為RequestContext方法在StartView中可以傳入)的,下面代碼是StartVIEw的完全版本:
public override void StartVIEw()
{
HttpResponseBase httpResponse = RequestContext.HttpResponse;
TagBuilder liTagBuilder = new TagBuilder("li");
if (mCreateLi)
{
httpResponse.Write(liTagBuilder.ToString(TagRenderMode.StartTag));
}
TagBuilder divTag = new TagBuilder("div");
divTag.AddCSSClass("option-group");
divTag.MergeAttribute("name", Name);
if (Attributes != null)
{
divTag.MergeAttributes(new RouteValueDictionary(Attributes));
}
TagBuilder labelTag = new TagBuilder("label");
labelTag.MergeAttribute("for", Name);
labelTag.SetInnerText(Name);
httpResponse.Write(labelTag.ToString(TagRenderMode.Normal));
httpResponse.Write(divTag.ToString(TagRenderMode.StartTag));
// Write out the radio buttons, let the MVC Helper do the hard work here
foreach (SelectListItem item in this.mSelectList)
{
string text = !string.IsNullOrEmpty(item.Text)
? item.Text
: item.Value;
httpResponse.Write(RequestContext.HtmlHelper.RadioButton(
Name, item.Value, item.Selected));
// Note: Because we are using HtmlHelper.RadioButton the <input>
// elements will have duplicate ids
// See: http://forums.asp.net/t/1363177.aspx
// In order to avoid this we could do this ourselves here
TagBuilder spanTag = new TagBuilder("span");
spanTag.SetInnerText(text);
httpResponse.Write(spanTag.ToString(TagRenderMode.Normal));
}
httpResponse.Write(divTag.ToString(TagRenderMode.EndTag));
if (this.mCreateLi)
{
httpResponse.Write(liTagBuilder.ToString(TagRenderMode.EndTag));
}
} 這裡的想法和HtmlText類如初一撤,那就是:所有的HTML代碼都在StartView方法中生成。因此這裡StartVIEw方法創建了一些Html tag,並遍歷mSelectList中的元素並通過ASP.Net MVC自帶的RadioButton擴展方法為每一個元素生成一個RadioButton。在重用這些方法時最好先重寫這些方法(譯者按:看上面代碼注釋)。
從上面代碼中的注釋可以看出,使用HtmlHelper.RadioButton擴展方法有一個明顯的bug,就是id和name用的是同一個值,這裡因為name屬性本來就應該為RadioButton設置成相同的這樣他們便可以邏輯上連成一組,但是id屬性是每個元素唯一擁有,這裡解決這個bug的方法是不用這個方法,但在這裡為了簡單起見我們先使用這個方法.上面創建的兩個Html helper對象都沒有用到EndVIEw方法,你可以已經開懷疑這個方法為什麼存在,在接下來的HtmlFieldSet的Helper我會給你展示EndVIEw的用途
-------------------------------------------
待續…