從開發者的角度來看,創建ASP.net MVC的VIEw是一件很爽的事,因為你可以精確控制最終生成的HTML。具有諷刺意味的是不得不寫出每一行Html代碼同時也是Asp.net MVC的View中讓人不爽的地方。讓我用我的一個經歷來告訴我創建ASP.Net MVC vIEw Helpers背後靈感的由來。由一小部分開發人員(包括我)和一個CSS設計人員(我們叫他Ricky)組成的小組,開始了一個新的ASP.Net MVC的項目,在項目開發過程中;我給頁面添加了一些TextBox和一些其他元素,我check-in了我的代碼,直到回家我也沒再想起過這事。隔夜早晨,剛上班時我就從CSS設計那裡收到一封郵件來通知我我必須按照他的CSS指導方針來寫Html,比如說對於textbox,必須遵循以下規則:
每個textbox必須內嵌在li標簽中
每一個textbox都必須有一個label標簽的for屬性與之對應
textbox必須使用input標簽並設置type屬性為text
對於這些要求我一一照做並修改我的代碼符合了後兩條規則,但我忘了關於li的指導方針,我很快更新了頁面並提交了我的代碼。幾天後,項目又推進了很多,Ricky來到我的辦公桌前並讓我看看我所做的改變。打開頁面,他開始一一列舉那些我不遵循它的UI規定的地方,有很多地方我都忽視了因為我甚至不知道這些指導方針的存在.在他指出這些後,我想:一定會有方法可以讓我們兩個人都如願以償.對於我來說只是需要html標簽的id,對於Ricky來說他需要我的HTML符合規范來讓他的CSS文件能夠選擇到合適的Html。所以我們引入了vIEw helper.
在我用ASP.Net MVC時我注意到我自己寫了很多純Html,比如div和span,同時伴隨使用了很多System.Web.Mvc.HtmlHelper來生成Html,比如說一個輸入名字的textbox:
<li>
<label for="FirstName">First name</label>
<%= Html.TextBox("FirstName") %>
<%= Html.ValidationMessage("FirstName", "*") %>
</li> 我就想,是不是能有一種方法來將上面的所有代碼融合在一起呢。這樣不僅讓我編程更加輕松,而且再也不用擔心Ricky給我設置的條條框框了。理想的情況下會滿足以下標准:
容易執行
重用性好
強制執行某些標准(比如Ricky的)
和標准的HtmlHelper擴展方法用起來沒太大區別
容易使用
在我們進入執行這個的細節之前如果你感覺這聽起來像又回到了Web Form時代,那就錯了。vIEw helper僅僅是在創建HTML的時候起輔助作用,而不是將HTML進行抽象。我關心的只是Html在頁面中的顯示效果以及使用Javascript的行為更輕松.而不是textbox是否放入li中,當我需要創建一個textbox時,我只需在vIEw中放入如下代碼:
<% Html.NewText("FirstName", "First name"); %>
我想聲明我僅僅是想將創建HTML延遲到另一個類中。使用VIEw helper我可以輕松做到這一點。首先我們先來看標准的HtmlHelper擴展方法如何做到這一點.
Html helper有兩種實現用法,大多數的使用方法都會如下:
<%= Html.TextBox("FirstName") %>
而還有一種用法和聲明一個form元素很相似:
<% using (Html.BeginForm()) { %>
<!-- Other elements here-->
<% } %> 上面兩種方法的主要區別是Html.TextBox僅僅返回一個string來注入到view中。這也是為什麼使用<%=而不是標准的的代碼塊。而另一種以對象作為返回類型的方法更老練許多,比如,System.Web.Mvc.Html.MvcForm,這個對象放入using語句.對象被創建時一些HTML就會被注入到vIEw中(嚴格說:並不是對象創建時,但很接近)還有一些事在對象被回收時將Html注入vIEw(也就是碰到”}”符號時).使用這種方法的好處是可以在using語句之間插入代碼。這使它的能力無疑比那些僅僅返回一個字符串注入頁面的方式要強大許多。
所以,我選擇第二種方法來實現我的VIEw Helpers.所以HtmlHelper擴展方法會實現我創建的IVIEwObject接口對象。類圖如下:
可以看到,IVIEwObject實現了System.IDisposable接口。這使實現如前面所提到和Html.BeginForm的使用方法類似所必須的。IViewObject有兩個方法,StartView和EndView.這兩個方法分別在對象創建時和對象回收時被調用.為了讓這些對象的創建更加容易我創建了一個抽象類來處理:執行方法,回收對象和在合適的時候調用EndVIEw方法。類圖如下:
上圖中的抽象類完整代碼如下:
public abstract class AbstractHtmlViewObject : IVIEwObject
{
PRivate bool mDisposed;
public AbstractHtmlViewObject(VIEwRequestContext requestContext, string name)
{
if (requestContext == null)
{ throw new ArgumentNullException("requestContext"); }
VIEwRequestContext = requestContext;
Name = name;
}
public IVIEwRequestContext RequestContext
{
get;
protected set;
}
#region IVIEwObject Members
public object Attributes { get; set; }
public string Name { get; set; }
public abstract void StartVIEw();
public abstract void EndVIEw();
#endregion
// based on System.Web.Mvc.HtmlHelper.GetModelStateValue
public object GetModelStateValue(string key, Type destinationType)
{
object result = null;
ModelState modelState;
if (VIEwRequestContext.HtmlHelper.VIEwData.ModelState.TryGetValue(
key, out modelState))
{
result = modelState.Value.ConvertTo(destinationType, null);
}
return result;
}
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!mDisposed)
{
mDisposed = true;
EndVIEw();
}
}
#endregion
} 如你所見上面AbstractHtmlVIEwObject對象不僅滿足了最上面提到的列表(Ricky那段裡),還包含了一些輔助類更容易擴展的東西。也就是它包含的一個屬性:RequestContext,這個屬性可以幫助我們很容易創建Html和擴展方法GetModelStateValue,我們會在後面詳細講述GetModelStateValue的使用方法。我們會在後面講述RequestContext的細節,這裡我們先看看如何創建我們先前討論的那個textbox。
-------------------------------------------
待續…