在企業應用開發場景,我們也許需要聚集一些模型/數據來源並將其呈現爲一個整體控制器。簡單起見,我們拿個需要輸入新學生姓名、年級、州(如圖一)的例子來說。
年級爲A、B和C,但如果我們能夠讓這些年級顯示爲第一類、第二類、第三類,而非ABC,那或許會頗有意義。因此在UI中,我們需要顯示第一二三類,但當保存數據是,我們需要在DB中保存相應的值,這同樣適用於“州”。當用戶點擊“州”下拉選擇圖標時,州列表豁然出現並展示所有州。當選中一個州時,它被顯示於UI中,但當保存到DB時,代碼名稱是諸如“Ca”代表“加利福尼亞”這樣保存的,如圖二所示。
圖一:用來輸入新生信息的表單。
圖二:當點擊州下來菜單時,如圖菜單出現。
ViewModel(視圖模型)的魔力:ViewModel允許你從一個或更多的數據模型/來源塑造多個實體到單個對象,優化了消耗和渲染。
圖三就表明了ViewModel的理念:
圖三:StudentViewModel集合了不同的模型和數據來源,提供信息給視圖作爲單個實體。
ViewModel的目的是將渲染單個對象的視圖,緩解對UI邏輯代碼的需求(原本是必需)。這意味着視圖唯一的責任就是渲染該單個ViewModel對象,在一個更乾淨的關注點分離予以幫助。關注點是應用程序獨特的一方面,它們有着特別的目的,而保持這些方面分離意味着你的應用程序更爲有組織,代碼更爲聚焦。將數據操作代碼置於其本身的位置,遠離視圖和控制器,增強關注點分離。在MVC中使用ViewModel將把你帶向更加簡單的可維護、可測試代碼。
現在我們來我們創建數據來源。第一個是年級,代碼如下。年級類是一個簡單的Dictionary對象包含兩類參數。類還包括Dictionary中所有成員的定義(如年級數據)。年級類唯一的屬性就是顯示列爲清單的年級。GradeSelectList屬性中的Dictionary<string, string>類相應地映射着年級縮寫及年級名稱。
public class Grades { public static SelectList GradeSelectList { get { return new SelectList(GradeDictionary, "Value", "Key"); } } public static readonly IDictionary<string, string> GradeDictionary = new Dictionary<string, string> { {"Choose…",""} , { "First Class", "A" } , { "Second Class", "B" } , { "Third Class", "c" } }; }
同樣的login用於“州”。StatesDictionary的代碼如下:
public class StatesDictionary { public static SelectList StateSelectList { get { return new SelectList(StateDictionary, "Value", "Key"); } } public static readonly IDictionary<string, string> StateDictionary = new Dictionary<string, string> { {"Choose…",""} , { "Alabama", "AL" } , { "Alaska", "AK" } , { "Arizona", "AZ" } , { "Arkansas", "AR" } , { "California", "CA" } , { "Colorado", "CO" } , { "Connecticut", "CT" } , { "Delaware", "DE" } , { "District of Columbia", "DC" } , { "Florida", "FL" } , { "Georgia", "GA" } , { "Hawaii", "HI" } , { "Idaho", "ID" } , { "Illinois", "IL" } , { "Indiana", "IN" } , { "Iowa", "IA" } , { "Kansas", "KS" } , { "Kentucky", "KY" } , { "Louisiana", "LA" } , { "Maine", "ME" } , { "Maryland", "MD" } , { "Massachusetts", "MA" } , { "Michigan", "MI" } , { "Minnesota", "MN" } , { "Mississippi", "MS" } , { "Missouri", "MO" } , { "Montana", "MT" } , { "Nebraska", "NE" } , { "Nevada", "NV" } , { "New Hampshire", "NH" } , { "New Jersey", "NJ" } , { "New Mexico", "NM" } , { "New York", "NY" } , { "North Carolina", "NC" } , { "North Dakota", "ND" } , { "Ohio", "OH" } , { "Oklahoma", "OK" } , { "Oregon", "OR" } , { "Pennsylvania", "PA" } , { "Rhode Island", "RI" } , { "South Carolina", "SC" } , { "South Dakota", "SD" } , { "Tennessee", "TN" } , { "Texas", "TX" } , { "Utah", "UT" } , { "Vermont", "VT" } , { "Virginia", "VA" } , { "Washington", "WA" } , { "West Virginia", "WV" } , { "Wisconsin", "WI" } , { "Wyoming", "WY" } // more states }; }
數據在小清單內並很少更改,如Grades,StatesDictionary類,存在於應用程序的所有類型中。
創建模型
現在我們需要一個模型來採集學生姓名、年級以及他/她來自哪個州(如圖一所示)。StudentModels類定義捕捉這些特徵於簡單的數據結構中。我們現在創建一個如下的StudentModels。在Models文件夾下創建一個文件StudentModels.cs並添加如下代碼:
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; namespace TagStatus.Models { public class StudentModels { [Display(Name = "Student Name here")] [Required()] public string StudentName { get; set; } public string Grade { get; set; } public string State { get; set; } } }
創建ViewModel
因爲數據來自需要被展示在視圖中的不同來源,如果你使用ViewModel,代碼和維護將會很簡單,ViewModels只是一個類。馬上開始,創建一個新的文件夾命名爲ViewModels並在其中添加一個新的代碼文件StudentViewModel.cs。要創建StudentViewModel,添加StudentModels、Grades和StatesDictionary作爲來自StudentViewModel的屬性。在如下源代碼中,StudentViewModel類包含新定義的屬性。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using TagStatus.Models; namespace TagStatus.ViewModels { public class StudentViewModel { public StudentModels Student { get; set; } public Grades Grades { get; set; } public StatesDictionary States { get; set; } public StudentViewModel(StudentModels student) { Student = student; Grades = new Grades(); States = new StatesDictionary(); } public StudentViewModel() { Student = new StudentModels(); ; Grades = new Grades(); States = new StatesDictionary(); } } }
創建控制器
在創建ViewModel之後,下一步就是在控制器中例示並將其返回到視圖。我們是在這個新的MVC4上做R&D(研發)因此你將看到一些亂碼。
如下代碼創建一個控制器傳送ViewModel到視圖,而我們關注的代碼是公共的ActionResult StudentInfo(),這在應用程序激活時被執行。
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using TagStatus.Models; using TagStatus.ViewModels; namespace TagStatus.Controllers { public class StudentController : Controller { //public string StudentName { get; set; } //public int Grade { get; set; } // // GET: /Student/ public ActionResult Index() { StudentModels stdm = new StudentModels(); StudentViewModel stdvm = new StudentViewModel(stdm); return View(stdvm); } //[AllowAnonymous] //public ActionResult StudentInfo() //{ // return View(); //} [AllowAnonymous] [HttpPost] public ActionResult StudentInfo(StudentModels model) { if (ModelState.IsValid) { model.StudentName = model.State; } return View(model); } [AllowAnonymous] public ActionResult StudentInfo() { StudentViewModel model = new StudentViewModel(); //model.StudentName = model.State; return View(model); } } }
在ASP.NET MVC 4視圖中創建HTML5 Mobile Form
在Visual Studio 2010中,點擊項目並新增Item命令以創建StudentInfo.cshtml。在視圖內,各種ASP.NET MVC 4 HTML Helpers通過渲染它們映射於ViewModel的數據類型的最適合的HTML元素來呈現StudentViewModel組件。比如, 年級渲染爲一個HTML下拉菜單由此用戶可以輕易選中一個項目,而非手動進入之。
StudentInfo.cshtml代碼如下:
@model TagStatus.ViewModels.StudentViewModel @{ ViewBag.Title = "Student Info"; } <h2> New Student </h2> @using (Html.BeginForm("Results","Home")) { @Html.ValidationSummary(true) <fieldset> <legend>Enter Student Information!</legend> <div class="editor-label"> @Html.LabelFor(model => model.Student.StudentName) </div> <div class="editor-field"> @Html.TextBoxFor(Model => Model.Student.StudentName) @Html.ValidationMessageFor(Model => Model.Student.StudentName) </div> <div class="editor-label"> @Html.LabelFor(model => model.Student.Grade) </div> <div class="editor-field"> @Html.DropDownListFor(Model => Model.Student.Grade, Grades.GradeSelectList) </div> <div class="editor-label"> @Html.LabelFor(model => model.Student.State) </div> <div class="editor-field"> @Html.DropDownListFor(Model => Model.Student.State, StatesDictionary.StateSelectList) </div> <p> <input type="submit" value="Save" /> </p> </fieldset> }
下載源代碼請點擊這裏在Global.asax文件做如下改變:
routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", //defaults: new { controller = "Tag", action = "Lookup", id = UrlParameter.Optional } defaults: new { controller = "Student", action = "StudentInfo", id = UrlParameter.Optional } //defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } );