共用方式為


檢查 ASP.NET MVC 如何 Scaffold DropDownList 協助程式

Rick Anderson

在 [方案總管] 中以滑鼠右鍵按一下 [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

)
  1. items包含清單中專案的 IEnumerable 。 在上述範例中,所 db.Genres傳回的內容類型清單。
  2. dataValueField:包含索引鍵值的 IEnumerable 清單中屬性的名稱。 在上述範例中, GenreIdArtistId
  3. 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 模型系結用來建立專輯物件。

Fiddler 工具的影像.

重構 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 方法中設定 ViewBagCreate兩行取代為 對方法的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協助程式將會使用 GenreIdViewBag 中的 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 物件名稱變更GenresArtists。 完成的程式代碼如下所示:

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 POSTEdit動作。

執行應用程式並編輯專輯。 將網址變更為使用 EditVM。 變更欄位,然後按 [ 儲存 ] 按鈕以確認程式代碼是否正常運作。

U R L 變更為編輯 V M 的影像

您應該使用哪種方法?

這三種方法都是可以接受的。 許多開發人員偏好使用 ViewBag明確地將 傳遞SelectListDropDownList 。 這種方法具有額外的優點,可讓您彈性地使用更適當的集合名稱。 其中一個注意事項是,您無法將 ViewBag SelectList 物件命名為與模型屬性相同的名稱。

有些開發人員偏好 ViewModel 方法。 其他人則認為 ViewModel 方法的更詳細標記和產生的 HTML 是缺點。

在本節中,我們已瞭解使用 DropDownList 搭配類別數據的三種方法。 在下一節中,我們將示範如何新增類別。