3.2.3 帶視圖模型的強類型視圖
當使用基於Razor的視圖時,視圖默認繼承兩個類型:System.Web.Mvc.WebViewPage或者System.Web.Mvc.WebViewPage<T>。泛型WebViewPage<T>繼承自WebViewPage,但是提供了一些非泛型WebViewPage類里沒有的獨特的補充。
下面展示了WebViewPage<T>的主干成員定義:
清單 3.3
{
public new AjaxHelper<TModel> Ajax { get; set; }
public new HtmlHelper<TModel> Html { get; set; }
public new TModel Model { get; }
public new ViewDataDictionary<TModel> ViewData { get; set; }
}
除了通過Model屬性在ViewData.Model之上提供了一個強類型包裝器外,WebViewPage<T>類還提供訪問關聯視圖的幫助器對象的強類型版本,AjaxHelper和HtmlHelper。
要使用強類型視圖,首先你必須確保控制器動作正確設置了ViewData.Model。在清單3.4里,我們獲取所有的留言記錄,顯示在列表頁面,並傳遞個人檔案的整個集合到View方法,此方法封裝了對ViewData.Model屬性的設置。
清單 3.4
{
var mostRecentEntries = ( from entry in _db.Entries
orderby entry.DateAdded descending
select entry).Take( 20);
var model = mostRecentEntries.ToList();
return View(model);
}
在與這個動作相應的Index視圖里,即使松散類型的WebViewPage類也能使用ViewData.Model屬性。但是這個屬性只是一個object類型,我們需要對它進行轉換以便有效地使用它。作為替代方案,我們能用@model關鍵詞指定模型的類型。
@using Guestbook.Models
@model List<GuestbookEntry>
通過用@model關鍵詞指定模型的類型,我們的視圖現在繼承自WebViewPage<T>而不是WebViewPage,我們有了一個強類型視圖。我們也用@using關鍵詞導入名字空間。在下一部分,我們將看到如何使用模型對象在視圖里顯示信息。
3.2.4
一般要在視圖里顯示信息,你可以使用HtmlHelper對象幫助獲得視圖模型以生成HTML。考慮下下面的清單,我們呈現了一個完整的留言板記錄。
清單 3.5
< dl >
< dt >Name: </ dt >
< dd >@Model.Name </ dd >
< dt >Date Added: </ dt >
< dd >@Model.DateAdded </ dd >
< dt >Message: </ dt >
< dd >@Model.Message </ dd >
</ dl >
< p >
@{
bool hasPermission =
(bool) ViewData["hasPermission"];
}
@if (hasPermission)
{
@Html.ActionLink("Edit", "Edit",
new {id = Model.Id})
}
@Html.ActionLink("Back to Entries", "Index")
</ p >
在這里,我們顯示在模型里傳遞的留言板詳細信息。接着,我們用Razor多行代碼語句從ViewData中獲取”hasPermission“的值。Razor多行語句用at符號后跟一個大括號來開始一個代碼塊:@{。最后,我們用一個Razor的if塊來有條件的顯示Edit鏈接。因為當在屏幕上顯示未編碼的用戶輸入時有可能遭到各種腳本攻擊,所以數據在呈現到屏幕以前默認是被自動編碼的。為了顯示未編碼的信息,我們可以使用Html.Raw方法強制顯示原生文本。
在登錄頁面,我們用一個視圖模型對象代表完整的表單,就像下面清單顯示的:
清單 3.6
{
[Required]
[Display(Name = " User name ")]
public string UserName { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = " Password ")]
public string Password { get; set; }
[Display(Name = " Remember me? ")]
public bool RememberMe { get; set; }
}
LogOnModel類很簡單,只包括必須的屬性。你在這里看到的特性是數據注解,關於它們更多的內容會在第4章學到。對於每一個屬性在登錄頁面都會顯示一個輸入元素,如圖3.2所示。
圖3.2 登錄頁
因為我們為登陸頁選擇了強類型視圖,我們能用內建的幫助器來為每一個輸入元素繪制出HTML。取代松散的綁定到代表動作參數的字符串,我們能利用基於表達式的HtmlHelper擴展創建各種類型的輸入元素,如下:
清單 3.7
@Html.ValidationSummary(true,
"Account creation was unsuccessful. " +
"Please correct the errors and try again.")
< div >
< fieldset >
< legend >Account Information </ legend >
< div class ="editor-label" >
@Html.LabelFor(m => m.UserName)
</ div >
< div class ="editor-field" >
@Html.TextBoxFor(m => m.UserName)
@Html.ValidationMessageFor(
m => m.UserName)
</ div >
< div class ="editor-label" >
@Html.LabelFor(m => m.Email)
</ div >
< div class ="editor-field" >
@Html.TextBoxFor(m => m.Email)
@Html.ValidationMessageFor(m => m.Email)
</ div >
< div class ="editor-label" >
@Html.LabelFor(m => m.Password)
</ div >
< div class ="editor-field" >
@Html.PasswordFor(m => m.Password)
@Html.ValidationMessageFor(m => m.Password)
</ div >
< div class ="editor-label" >
@Html.LabelFor(m => m.ConfirmPassword)
</ div >
< div class ="editor-field" >
@Html.PasswordFor(m => m.ConfirmPassword)
@Html.ValidationMessageFor(m => m.ConfirmPassword)
</ div >
< p >
< input type ="submit" value ="Register" />
</ p >
</ fieldset >
</ div >
}
在先前的清單里,我們為強類型視圖頁面使用了幾個HtmlHelper的擴展方法,這些方法分別針對標簽,輸入框和驗證信息。取代用松散類型的字符串代表屬性,在asp.net mvc 1里就是這樣使用( @Html.TextBox("UserName")),這些幫助器方法利用c# 3.5的表達式來生成HTML。因為這些生成的HTML元素需要與對象中的屬性匹配,所以只適合用於原生的類型和與表達式一起使用的對象。
在清單3.7里Html.LabelFor和Html.TextBoxFor方法為UserNam屬性產生HTML:
清單3.8
< input id ="UserName" name ="UserName" type ="text" value ="" />
為了讓我們的頁面通過可訪問性驗證,每一個輸入元素(如清單3.8的第二行)需要包含一個相應的標簽元素(如第一行)。因為我們的標簽和輸入元素都是用表達式生成的,所以我們不再擔心標簽和輸入名稱硬編碼的問題。
表格3.1列出了用於強類型視圖的HtmlHelper的各種擴展方法。
表格3.1
- DisplayFor
- DisplayTextFor
- EditorFor
- CheckBoxFor
- DropDownListFor
- HiddenFor
- LabelFor
- ListBoxFor
- PasswordFor
- RadioButtonFor
- TextAreaFor
- TextBoxFor
- ValidateFor
- ValidationMessageFor
由於我們的表單是用強類型視圖生成的,我們可以在設計表單post的動作上利用這一點。不用枚舉每個輸入域的值作為動作方法的參數,我們可以把所有的參數都綁定到與呈現視圖同一個的視圖模型上,如下所示。
清單3.9
{
// Action method body here
...
}
正如你看見的,LogOn動作方法使用單個LogOnModel對象,以及要返回的URL,而不是為表單里的每個輸入元素用一個方法參數。
HtmlHelper擴展可能是強大的,但如果只依賴這些擴展產生HTML,在視圖里仍然會引入了相當多的重復。例如,如果每個輸入元素需要一個相應的標簽,為什么不總是包括它呢?每個用戶界面是不同的,所以MVC團隊不能預知每個人所用的輸入和標簽元素的布局。雖然每個輸入元素都應該有一個標簽,現有的創建輸入元素的輔助方法不適合包括標簽元素。作為替代方案,我們可以利用在asp.net mvc 2里介紹的功能——模版——用一種標准化的方式產生HTML。