從開發人員的角度來看,建立Asp.net MVC的View是一件很爽的事,因為你可以精確控制最終產生的HTML。具有諷刺意味的是不得不寫出每一行HTML代碼同時也是Asp.net MVC的View中讓人不爽的地方。讓我用我的一個經曆來告訴我建立ASP.Net MVC view Helpers背後靈感的由來。由一小部分開發人員(包括我)和一個CSS設計人員(我們叫他Ricky)組成的小組,開始了一個新的Asp.net MVC的項目,在項目開發過程中;我給頁面添加了一些TextBox和一些其他元素,我check-in了My Code,直到回家我也沒再想起過這事。隔夜早晨,剛上班時我就從CSS設計那裡收到一封郵件來通知我我必須按照他的CSS指導方針來寫HTML,比如說對於textbox,必須遵循以下規則:
- 每個textbox必須內嵌在li標籤中
- 每一個textbox都必須有一個label標籤的for屬性與之對應
- textbox必須使用input標籤並設定type屬性為text
對於這些要求我一一照做並修改My Code符合了後兩條規則,但我忘了關於li的指導方針,我很快更新了頁面並提交了My Code。幾天后,項目又推進了很多,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。
-------------------------------------------
待續…
原文連結:http://mvcviewhelpers.codeplex.com/
translated by CareySon