一.前言
如果你使用ASP.NET MVC制作后台一定會愛上它的EditorForModal、DisplayForModal和LabelForModal方法,因為這些方法可以將模型直接變成對應的標簽,省了不少事,但是對於一些苛刻的人來說,一定想自定義,下面我們會先介紹如何使用,然后介紹如何自定義。
二.正文
1.輸出模型
首先我們要新建一個Home控制器,對應的還要有一個Index動作,和Index視圖,接着我們在Modal下新建一個Address類:
1 namespace MvcStudy.Models 2 { 3 public class Address 4 { 5 public string Line1 { get; set; } 6 public string Line2 { get; set; } 7 public string City { get; set; } 8 public string PostalCode { get; set; } 9 public string Country { get; set; } 10 } 11 }
接着再新建一個Role枚舉:
1 namespace MvcStudy.Models 2 { 3 public enum Role 4 { 5 Admin, 6 User, 7 Guest 8 } 9 }
最后新建一個Person類:
1 namespace MvcStudy.Models 2 { 3 public partial class Person 4 { 5 public int PersonId { get; set; } 6 public string FirstName { get; set; } 7 public string LastName { get; set; } 8 public DateTime BirthDate { get; set; } 9 public Address HomeAddress { get; set; } 10 public bool IsApproved { get; set; } 11 public Role Role { get; set; } 12 } 13 }
然后我們在Home控制器的Index方法中寫入如下代碼:
1 namespace MvcStudy.Controllers 2 { 3 public class HomeController : Controller 4 { 5 public ActionResult Index() 6 { 7 Person p = new Person 8 { 9 PersonId = 1, 10 HomeAddress = new Address 11 { 12 City = "zj", 13 Country = "js", 14 Line1 = "111", 15 Line2 = "222", 16 PostalCode = "asdsa" 17 }, 18 Role = Models.Role.User, 19 BirthDate = DateTime.Now, 20 FirstName = "y", 21 LastName = "zf", 22 IsApproved = false 23 }; 24 return View(p); 25 } 26 } 27 }
同時還要確保Index視圖的model的類型是Person類型,如果不是可以在最頂部加上如下代碼:
@model MvcStudy.Models.Person
接着我們打開Index視圖,在其中寫入@Html.EditorForModel(),運行之后我們可以看到頁面上輸出了該類的屬性:
圖 1.1

但是我們可以發現PersonId也輸出了而且還可以編輯,這可不是我們所想要的,所以我們要在模型的PersonId上加上HiddenInput注解屬性,重新編譯后我們可以看到PersonId已經不可編輯了:

但是有時我們根本不想讓它顯示出來,這個時候我們需要修改HiddenInput注解屬性,將DisplayValue設置為false即可,但是你可以通過查看頁面的html源代碼看到存在一個隱藏的input標簽,當然如果你根本不想讓這個屬性存在於頁面中我們可以將HiddenInput修改成[ScaffoldColumn(false)]即可。
通過圖1.1我們可以發現label中顯示的都是該屬性的名稱,並不符合實際的使用習慣,所以我們還要給這些屬性加上該顯示的名稱:
1 namespace MvcStudy.Models 2 { 3 public partial class Person 4 { 5 [ScaffoldColumn(false)] 6 public int PersonId { get; set; } 7 [Display(Name = "名")] 8 public string FirstName { get; set; } 9 [Display(Name = "姓")] 10 public string LastName { get; set; } 11 [Display(Name = "生日")] 12 public DateTime BirthDate { get; set; } 13 public Address HomeAddress { get; set; } 14 [Display(Name = "是否啟用")] 15 public bool IsApproved { get; set; } 16 [Display(Name = "角色")] 17 public Role Role { get; set; } 18 } 19 }
重新編譯之后我們可以看到頁面變成了如下所示:

我們可以看到字符串都使用input去呈現,但是有時候這樣並不符合實際情況,那么我們就要修改屬性的DataType類型,比如我們將姓修改成富文本,修改Person代碼:
1 namespace MvcStudy.Models 2 { 3 public partial class Person 4 { 5 [ScaffoldColumn(false)] 6 public int PersonId { get; set; } 7 [Display(Name = "名")] 8 [DataType(DataType.Text)] 9 public string FirstName { get; set; } 10 [Display(Name = "姓")] 11 [DataType(DataType.MultilineText)] 12 public string LastName { get; set; } 13 [Display(Name = "生日")] 14 [DataType(DataType.DateTime)] 15 public DateTime BirthDate { get; set; } 16 public Address HomeAddress { get; set; } 17 [Display(Name = "是否啟用")] 18 public bool IsApproved { get; set; } 19 [Display(Name = "角色")] 20 public Role Role { get; set; } 21 } 22 }
然后我們重新編譯之后可以看到最終想要的效果了,當然這些都是ASP.NET MVC根據類型自動判斷的並選擇對應的模板,所以我們也可以直接設置屬性使用的模板,這樣我們可以通過使用UIHint去指定模板的名稱(其實就是視圖的名稱)。
2.將元數據用於分布類
現在開發網站模型都是工具自動生成了,如果按照我們上面的這種方法,一旦重新生成模型了,那么我們就需要重新將注解屬性加上上去,既費時又費力。幸好ASP.NET MVC提供了解決方案,我們只需要為那個模型類加上一個分布類即可,比如下面我們將Person的注解屬性全部轉移到分布類中:
筆者新建了一個名為PersonExt的類:
1 namespace MvcStudy.Models 2 { 3 public class PersonDataBindSource 4 { 5 [ScaffoldColumn(false)] 6 public int PersonId { get; set; } 7 [Display(Name = "名")] 8 [DataType(DataType.Text)] 9 public string FirstName { get; set; } 10 [Display(Name = "姓")] 11 [DataType(DataType.MultilineText)] 12 public string LastName { get; set; } 13 [Display(Name = "生日")] 14 [DataType(DataType.DateTime)] 15 public DateTime BirthDate { get; set; } 16 public Address HomeAddress { get; set; } 17 [Display(Name = "是否啟用")] 18 public bool IsApproved { get; set; } 19 [Display(Name = "角色")] 20 public Role Role { get; set; } 21 } 22 23 [MetadataType(typeof(PersonDataBindSource))] 24 public partial class Person 25 { 26 } 27 }
然后我們就可以刪除Person類的注解屬性了,重新編譯可以看到最后的輸出還是一樣的。細心的讀者會發現Address屬性並沒有輸出,這是因為輔助器並不會遞歸查找,所以我們需要在Index視圖中寫入@Html.EditorFor(x => x.HomeAddress)即可。
3.使用自定義模板
通過上面的例子我們發現Role呈現出來的是輸入框,但實際上應該是選擇的,這個時候我們就需要自定義模板,首先我們在Views/Shared下新建一個EditorTemplates文件夾,然后新建一個Role視圖,強類型為Role類型,並且不使用母版。然后在視圖中寫入如下代碼:
1 <select id="Role" name="Role"> 2 @foreach (Role value in Enum.GetValues(typeof(Role))) 3 { 4 <option value="@value" @(Model == value ?"selected='selected'":"")>@value</option> 5 } 6 </select>
接着刷新頁面,我們就可以看到Role變成了下拉選擇了:

當然文件夾的名稱是一種規范,如果需要自定了Display模板,則新建DisplayTemplates文件夾,然后根據類型新建對應名稱的視圖,當然我們也可以新建一些ASP.NET MVC已經提供了模板的類型,而ASP.NET MVC會首先使用我們的模板而不是自帶的。當然我們也可以通過UIHint指定模板。有時候我們需要設置標簽的Id,那么我們可以通過ViewData.TemplateInfo.GetFullHtmlFieldId方法獲取Id而ViewData.TemplateInfo.GetFullHtmlFieldName用來獲取name,如果你對這些還不夠滿意,那么你可以通過ViewData.ModelMetadata中的屬性獲取模型中屬性的全部信息。
4.傳遞參數到模板中
有時候我們需要在模型模板與模板之間傳遞自定義的參數,那么下面的方式將可以實現。首先我們在PersonExt文件中給BirthDate屬性加上如下的注解屬性:
1 [AdditionalMetadata("val","test")]
該注解屬性的第一個參數是Key,第二個是對應的參數,然后我們在Views/Shared/EditorTemplates中新建一個DateTime視圖,並在其中寫入如下代碼:
1 @model DateTime 2 3 @{ 4 Layout = null; 5 } 6 7 @Html.TextBox(ViewData.TemplateInfo.GetFullHtmlFieldName("a"), Model) 8 @if(ViewData.ModelMetadata.AdditionalValues.ContainsKey("val")) 9 { 10 @Html.Label(ViewData.ModelMetadata.AdditionalValues["val"].ToString()) 11 }
這里ModelMetadata的AdditionalValues屬性保存了傳遞的自定義參數,上面我們首先判斷是否存在Key為val的參數,然后在將val的值顯示出來,重新編譯之后刷新頁面我們可以看到如下的效果:

