Поделиться через


Общие сведения о формировании шаблона вспомогательного приложения DropDownList в ASP.NET MVC

Рик Андерсон

В Обозреватель решений щелкните правой кнопкой мыши папку "Контроллеры", а затем выберите "Добавить контроллер". Назовите контроллер 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, позволяет CRUD (Создать, чтение, обновление, удаление) доступ к альбомам в базе данных хранилища музыки. Схема для сведений о альбоме показана ниже:

Изображение схемы альбома

Таблица Albums не хранит жанр и описание альбома, он хранит внешний ключ к Genres таблице. Таблица Genres содержит имя и описание жанра. Аналогичным образом Albums , таблица не содержит названия исполнителей альбома, но внешний ключ к Artists таблице. Таблица Artists содержит имя художника. При проверке данных в Albums таблице можно увидеть, что каждая строка содержит внешний ключ Genres к таблице и внешний ключ к Artists таблице. На рисунке Albums ниже показаны некоторые данные таблицы из таблицы.

Изображение некоторых данных из таблицы

Тег выбора HTML

Элемент HTML (созданный вспомогательным элементом HTML <select> DropDownList ) используется для отображения полного списка значений (например, списка жанров). При изменении форм, когда известно текущее значение, список выбора может отображать текущее значение. Мы видели это ранее, когда мы установили выбранное значение комедии. Список выбора идеально подходит для отображения данных категории или внешнего ключа. Элемент <select> внешнего ключа "Жанр" отображает список возможных имен жанров, но при сохранении формы свойство "Жанр" обновляется значением внешнего ключа "Жанр", а не отображаемым именем жанра. На изображении ниже выбран жанр Диско , и художник Донна Лето.

Изображение выбранного жанра

Изучение шаблона MVC ASP.NET

Откройте файл 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. элементы: IEnumerable, содержащий элементы в списке. В приведенном выше примере список жанров, возвращаемых db.Genres.
  2. dataValueField: имя свойства в списке IEnumerable , содержащего значение ключа. В приведенном выше GenreId примере и ArtistId.
  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 не было передано никакой модели, поэтому представление получает пустую Album модель. На этом этапе мы создадим новый альбом, поэтому у нас нет Album данных для него.

Перегрузка Html.DropDownList , показанная выше, принимает имя поля для привязки к модели. Он также использует это имя для поиска объекта ViewBag , содержащего объект SelectList . Используя эту перегрузку, необходимо присвоить имя объекту GenreIdViewBag SelectList. Второй параметр (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 объект, созданный системой привязки модели MVC ASP.NET из опубликованных значений формы. При отправке нового альбома, если состояние модели допустимо и нет ошибок базы данных, новый альбом добавляется в базу данных. На следующем рисунке показано создание нового альбома.

Изображение, показывающее создание нового альбома

Вы можете использовать средство fiddler для изучения опубликованных значений формы, которые ASP.NET привязка модели MVC использует для создания объекта альбома.

Изображение средства Fiddler.

Рефакторинг создания viewBag SelectList

Edit Методы и HTTP POST Create методы имеют идентичный код для настройки SelectList в ViewBag. В духе 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);

}

Замените две строки, заданные ViewBag в каждом из Create Edit методов вызовом 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, используют следующую перегрузку 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 будет использовать GenreIdSelectList в ViewBag. В приведенной ниже перегрузке 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)

Запустите приложение и щелкните ссылку "Администратор" , а затем перейдите к джазовой альбому и выберите ссылку "Изменить ".

Изображение выбора джазового альбома для редактирования

Вместо отображения джаза в качестве выбранного в настоящее время жанра, Рок отображается. Если строковый аргумент (свойство для привязки) и объект 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

Создайте класс в папке ViewModels с именем AlbumSelectListViewModel. Замените код в 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.
  • Вспомогательный элемент BeginForm в представлении EditVM явно публикуется обратно в Edit метод действия. Записав действие обратно в Edit действие, нам не нужно писать HTTP POST EditVM действие и повторно использовать HTTP POST Edit действие.

Запустите приложение и измените альбом. Измените используемый URL-адрес EditVM. Измените поле и нажмите кнопку "Сохранить ", чтобы убедиться, что код работает.

Изображение с изменением U R L на Edit V M

Какой подход следует использовать?

Показаны все три подхода. Многие разработчики предпочитают явно передавать их SelectList DropDownList в использование ViewBag. Этот подход позволяет повысить гибкость использования более подходящего имени для коллекции. Одно из предостережение заключается в том, что объект нельзя назвать ViewBag SelectList таким же именем, как свойство модели.

Некоторые разработчики предпочитают подход ViewModel. Другие считают более подробной разметкой и созданным HTML-кодом подхода ViewModel к недостатку.

В этом разделе мы узнали три подхода к использованию DropDownList с данными категории. В следующем разделе мы покажем, как добавить новую категорию.