檢查 ASP.NET MVC 如何 Scaffold DropDownList 協助程式
在 [方案總管] 中以滑鼠右鍵按一下 [Controllers] 資料夾,然後選取 [新增控制器]。 將控制器 命名為 StoreManagerController。 設定 [新增控制器] 對話框的選項,如下圖所示。
編輯 StoreManager\Index.cshtml 檢視並移除 AlbumArtUrl
。 拿掉 AlbumArtUrl
會使簡報更容易閱讀。 已完成的程式碼如下所示。
@model IEnumerable<MvcMusicStore.Models.Album>
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
@Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th>
Genre
</th>
<th>
Artist
</th>
<th>
Title
</th>
<th>
Price
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.Genre.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Artist.Name)
</td>
<td>
@Html.DisplayFor(modelItem => item.Title)
</td>
<td>
@Html.DisplayFor(modelItem => item.Price)
</td>
<td>
@Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) |
@Html.ActionLink("Details", "Details", new { id=item.AlbumId }) |
@Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })
</td>
</tr>
}
</table>
開啟 Controllers\StoreManagerController.cs 檔案,並尋找 Index
方法。 OrderBy
新增 子句,讓專輯依價格排序。 完整的程序代碼如下所示。
public ViewResult Index()
{
var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist)
.OrderBy(a => a.Price);
return View(albums.ToList());
}
依價格排序可讓您更輕鬆地測試資料庫的變更。 當您測試編輯和建立方法時,可以使用低價,以便先顯示儲存的數據。
開啟 StoreManager\Edit.cshtml 檔案。 在圖例標籤後面新增下列這一行。
@Html.HiddenFor(model => model.AlbumId)
下列程式代碼顯示此變更的內容:
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Album</legend>
@Html.HiddenFor(model => model.AlbumId)
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", String.Empty)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<!-- Items removed for brevity. -->
}
AlbumId
必須變更專輯記錄。
按 CTRL+F5 執行應用程式。 選取 [ 管理] 鏈接,然後選取 [ 新建 ] 連結以建立新的專輯。 確認已儲存專輯資訊。 編輯專輯,並確認您所做的變更會保存。
專輯架構
StoreManager
MVC Scaffolding 機制所建立的控制器允許 CRUD (建立、讀取、更新、刪除) 存取音樂存放區資料庫中的專輯。 專輯資訊的架構如下所示:
數據表 Albums
不會儲存專輯內容類型和描述,它會儲存數據表的 Genres
外鍵。 數據表 Genres
包含內容類型名稱和描述。 同樣地, Albums
數據表不包含專輯藝術家名稱,而是數據表的 Artists
外鍵。 表格 Artists
包含藝術家的名稱。 如果您檢查數據表中的數據 Albums
,您可以看到每個數據列都包含數據表的 Genres
外鍵,以及數據表的 Artists
外鍵。 下圖顯示數據表中的 Albums
一些數據表數據。
HTML 選取標籤
HTML 元素(由 HTML <select>
DropDownList 協助程式所建立)用來顯示完整的值清單(例如內容類型清單)。 針對編輯表單,當目前的值已知時,選取清單可以顯示目前的值。 當我們將選取的值設定為 Comedy 時,我們先前看到這個值。 選取清單很適合用來顯示類別或外鍵數據。 <select>
內容類型外鍵的 元素會顯示可能的內容類型名稱清單,但當您儲存表單時,內容類型屬性會以內容類型外鍵值更新,而不是顯示的內容類型名稱。 在下圖中,選取的流派是 迪斯科, 而藝術家是 唐娜·薩默。
檢查 ASP.NET MVC Scaffolded 程式代碼
開啟 Controllers\StoreManagerController.cs 檔案,並尋找 HTTP GET Create
方法。
public ActionResult Create()
{
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
return View();
}
方法會將 Create
兩 個 SelectList 物件新增至 ViewBag
,一個物件包含內容類型資訊,另一個則包含藝術家資訊。 上述使用的 SelectList 建構函式多載採用三個自變數:
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField
)
- items: 包含清單中專案的 IEnumerable 。 在上述範例中,所
db.Genres
傳回的內容類型清單。 - dataValueField:包含索引鍵值的 IEnumerable 清單中屬性的名稱。 在上述範例中,
GenreId
和ArtistId
。 - dataTextField:IEnumerable 清單中的屬性名稱,其中包含要顯示的資訊。 在藝術家和內容類型數據表中,
name
會使用欄位。
開啟 Views\StoreManager\Create.cshtml 檔案,並檢查Html.DropDownList
內容類型字段的協助程式標記。
@model MvcMusicStore.Models.Album
@* Markup removed for clarity.*@
@Html.DropDownList("GenreId", String.Empty)
第一行顯示建立檢視採用 Album
模型。 在上述方法中 Create
,未傳遞任何模型,因此檢視會取得 Null Album
模型。 此時,我們正在建立一張新專輯,因此我們沒有任何 Album
數據可供使用。
上面顯示的 Html.DropDownList 多載會採用要系結至模型的功能變數名稱。 它也會使用此名稱來尋找包含 SelectList 物件的 ViewBag 物件。 使用此多載,您必須將 ViewBag SelectList 物件 GenreId
命名為 。 第二個參數 (String.Empty
) 是未選取任何專案時要顯示的文字。 這正是我們在建立新專輯時想要的。 如果您移除了第二個參數,並使用下列程式代碼:
@Html.DropDownList("GenreId")
選取清單會預設為範例中的第一個專案或 Rock。
檢查 HTTP POST Create
方法。
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album)
{
if (ModelState.IsValid)
{
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name",
album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name",
album.ArtistId);
return View(album);
}
此方法的 Create
這個多載會從 album
張貼的窗體值,採用由 ASP.NET MVC 模型系結系統所建立的物件。 當您提交新專輯時,如果模型狀態有效且沒有資料庫錯誤,新專輯就會新增資料庫。 下圖顯示新專輯的建立。
您可以使用 fiddler 工具來 檢查張貼的表單值,ASP.NET MVC 模型系結用來建立專輯物件。
.
重構 ViewBag SelectList 建立
Edit
方法和 HTTP POST Create
方法都有相同的程序代碼,以在 ViewBag 中設定 SelectList。 以 DRY 的精神,我們將重構此程式代碼。 我們稍後會使用此重構的程序代碼。
建立新的方法,將內容類型和藝術家 SelectList 新增至 ViewBag。
private void SetGenreArtistViewBag(int? GenreID = null, int? ArtistID = null) {
if (GenreID == null)
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");
else
ViewBag.GenreId = new SelectList(db.Genres.ToArray(), "GenreId", "Name", GenreID);
if (ArtistID == null)
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");
else
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", ArtistID);
}
將和方法中每個和 Edit
方法中設定 ViewBag
的Create
兩行取代為 對方法的SetGenreArtistViewBag
呼叫。 已完成的程式碼如下所示。
//
// GET: /StoreManager/Create
public ActionResult Create() {
SetGenreArtistViewBag();
return View();
}
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album) {
if (ModelState.IsValid) {
db.Albums.Add(album);
db.SaveChanges();
return RedirectToAction("Index");
}
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
//
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id) {
Album album = db.Albums.Find(id);
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(Album album) {
if (ModelState.IsValid) {
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
SetGenreArtistViewBag(album.GenreId, album.ArtistId);
return View(album);
}
建立新的專輯並編輯專輯,以確認變更是否正常運作。
明確將 SelectList 傳遞至 DropDownList
ASP.NET MVC Scaffolding 所建立的建立和編輯檢視會使用下列 DropDownList 多載:
public static MvcHtmlString DropDownList(
this HtmlHelper htmlHelper,
string name, // The name of the ViewModel property to bind.
string optionLabel // The string added to the top of the list
// typically String.Empty or "Select a Genre"
)
DropDownList
建立檢視的標記如下所示。
@Html.DropDownList("GenreId", String.Empty)
因為 的 ViewBag
SelectList
屬性名為 GenreId
,因此DropDownList協助程式將會使用 GenreId
ViewBag 中的 SelectList。 在下列 DropDownList 多載中, SelectList
會明確傳入 。
public static MvcHtmlString DropDownList(
this HtmlHelper htmlHelper,
string name, // The name of the ViewModel property to bind.
IEnumerable selectList // The SelectList
)
開啟 Views\StoreManager\Edit.cshtml 檔案,然後使用上述多載,變更 DropDownList 呼叫以明確傳入 SelectList。 請針對 [內容類型] 類別執行此動作。 完成的程式代碼如下所示:
@Html.DropDownList("GenreId", ViewBag.GenreId as SelectList)
執行應用程式並按下 [系統管理員 ] 鏈接,然後流覽至爵士專輯,然後選取 [ 編輯] 連結。
除了將 Jazz 顯示為目前選取的內容類型,而是會顯示 Rock。 當字串自變數 (要系結的屬性) 和 SelectList 物件具有相同的名稱時,不會使用選取的值。 未提供選取的值時,瀏覽器預設為 SelectList 中的第一個專案(如上述範例中的 Rock)。 這是DropDownList協助程式已知的限制。
開啟 Controllers\StoreManagerController.cs 檔案,並將 SelectList 物件名稱變更為 Genres
和 Artists
。 完成的程式代碼如下所示:
private void SetGenreArtistViewBag(int? GenreID = null, int? ArtistID = null) {
if (GenreID == null)
ViewBag.Genres = new SelectList(db.Genres, "GenreId", "Name");
else
ViewBag.Genres = new SelectList(db.Genres.ToArray(), "GenreId", "Name", GenreID);
if (ArtistID == null)
ViewBag.Artists = new SelectList(db.Artists, "ArtistId", "Name");
else
ViewBag.Artists = new SelectList(db.Artists, "ArtistId", "Name", ArtistID);
}
「內容類型」和「藝術家」名稱是類別的較佳名稱,因為它們不僅包含每個類別的標識碼。 我們先前所做的重構得到了回報。 我們的變更不是在四種方法中變更 ViewBag ,而是隔離至 SetGenreArtistViewBag
方法。
變更建立和編輯檢視中的DropDownList呼叫,以使用新的SelectList名稱。 編輯檢視的新標記如下所示:
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", ViewBag.Genres as SelectList)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("ArtistId", ViewBag.Artists as SelectList)
@Html.ValidationMessageFor(model => model.ArtistId)
</div>
[建立] 檢視需要一個空字串,以防止顯示 SelectList 中的第一個專案。
<div class="editor-label">
@Html.LabelFor(model => model.GenreId, "Genre" )
</div>
<div class="editor-field">
@Html.DropDownList("GenreId", ViewBag.Genres as SelectList, String.Empty)
@Html.ValidationMessageFor(model => model.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("ArtistId", ViewBag.Artists as SelectList, String.Empty)
@Html.ValidationMessageFor(model => model.ArtistId)
</div>
建立新的專輯並編輯專輯,以確認變更是否正常運作。 選取具有 Rock 以外的內容類型的專輯,以測試編輯程式代碼。
搭配DropDownList協助程式使用檢視模型
在名為 AlbumSelectListViewModel
的 ViewModels 資料夾中建立新的類別。 將類別中的 AlbumSelectListViewModel
程式碼取代為下列專案:
using MvcMusicStore.Models;
using System.Web.Mvc;
using System.Collections;
namespace MvcMusicStore.ViewModels {
public class AlbumSelectListViewModel {
public Album Album { get; private set; }
public SelectList Artists { get; private set; }
public SelectList Genres { get; private set; }
public AlbumSelectListViewModel(Album album,
IEnumerable artists,
IEnumerable genres) {
Album = album;
Artists = new SelectList(artists, "ArtistID", "Name", album.ArtistId);
Genres = new SelectList(genres, "GenreID", "Name", album.GenreId);
}
}
}
建構函式 AlbumSelectListViewModel
會採用專輯、藝術家和內容類型的清單,並建立包含專輯的物件,以及 SelectList
用於內容類型和藝術家的 。
建置專案,以便在 AlbumSelectListViewModel
下一個步驟中建立檢視時使用 。
EditVM
將 方法新增至 StoreManagerController
。 已完成的程式碼如下所示。
//
// GET: /StoreManager/EditVM/5
public ActionResult EditVM(int id) {
Album album = db.Albums.Find(id);
if (album == null)
return HttpNotFound();
AlbumSelectListViewModel aslvm = new AlbumSelectListViewModel(album, db.Artists, db.Genres);
return View(aslvm);
}
以滑鼠右鍵按兩下 AlbumSelectListViewModel
,選取 [解析],然後使用 MvcMusicStore.ViewModels;。
或者,您也可以新增下列using語句:
using MvcMusicStore.ViewModels;
以滑鼠右鍵按下 EditVM
並選取 [ 新增檢視]。 使用如下所示的選項。
選取 [新增],然後將 Views\StoreManager\EditVM.cshtml 檔案的內容取代為下列內容:
@model MvcMusicStore.ViewModels.AlbumSelectListViewModel
@{
ViewBag.Title = "EditVM";
}
<h2>Edit VM</h2>
@using (Html.BeginForm("Edit","StoreManager",FormMethod.Post)) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Album</legend>
@Html.HiddenFor(model => model.Album.AlbumId )
<div class="editor-label">
@Html.LabelFor(model => model.Album.GenreId, "Genre")
</div>
<div class="editor-field">
@Html.DropDownList("Album.GenreId", Model.Genres)
@Html.ValidationMessageFor(model => model.Album.GenreId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.ArtistId, "Artist")
</div>
<div class="editor-field">
@Html.DropDownList("Album.ArtistId", Model.Artists)
@Html.ValidationMessageFor(model => model.Album.ArtistId)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.Title)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.Title)
@Html.ValidationMessageFor(model => model.Album.Title)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.Price)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.Price)
@Html.ValidationMessageFor(model => model.Album.Price)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Album.AlbumArtUrl)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Album.AlbumArtUrl)
@Html.ValidationMessageFor(model => model.Album.AlbumArtUrl)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
標記 EditVM
與原始 Edit
標記非常類似,但有下列例外狀況。
- 檢視中的
Edit
模型屬性是表單model.property
(例如 ,model.Title
。 檢視中的EditVm
模型屬性是表單model.Album.property
(例如 ,model.Album.Title
。 這是因為檢視EditVM
是傳遞的容器,Album
而不是Album
檢視中的Edit
。 - DropDownList 第二個參數來自檢視模型,而不是 ViewBag。
- 檢視中的
EditVM
BeginForm協助程式會明確張貼回Edit
動作方法。 藉由回傳至Edit
動作,我們不需要撰寫HTTP POST EditVM
動作,而且可以重複使用HTTP POST
Edit
動作。
執行應用程式並編輯專輯。 將網址變更為使用 EditVM
。 變更欄位,然後按 [ 儲存 ] 按鈕以確認程式代碼是否正常運作。
您應該使用哪種方法?
這三種方法都是可以接受的。 許多開發人員偏好使用 ViewBag
明確地將 傳遞SelectList
至 DropDownList
。 這種方法具有額外的優點,可讓您彈性地使用更適當的集合名稱。 其中一個注意事項是,您無法將 ViewBag SelectList
物件命名為與模型屬性相同的名稱。
有些開發人員偏好 ViewModel 方法。 其他人則認為 ViewModel 方法的更詳細標記和產生的 HTML 是缺點。
在本節中,我們已瞭解使用 DropDownList 搭配類別數據的三種方法。 在下一節中,我們將示範如何新增類別。