Compartilhar via


Examinar como o ASP.NET MVC faz scaffold do auxiliar DropDownList

por Rick Anderson

No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Controladores e selecione Adicionar Controlador. Nomeie o controlador StoreManagerController. Defina as opções para a caixa de diálogo Adicionar controlador, conforme mostrado na imagem abaixo.

Imagem da caixa de diálogo Adicionar Controlador do Gerenciador de Soluções

Edite a exibição StoreManager\Index.cshtml e remova AlbumArtUrlo . A remoção AlbumArtUrl tornará a apresentação mais legível. O código completo é mostrado abaixo.

@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>

Abra o arquivo Controllers\StoreManagerController.cs e localize o Index método. Adicione a OrderBy cláusula para que os álbuns sejam classificados por preço. O código completo é mostrado abaixo.

public ViewResult Index()
{

    var albums = db.Albums.Include(a => a.Genre).Include(a => a.Artist)

        .OrderBy(a => a.Price);

    return View(albums.ToList());

}

A classificação por preço facilitará o teste de alterações no banco de dados. Ao testar os métodos de edição e criação, você pode usar um preço baixo para que os dados salvos apareçam primeiro.

Abra o arquivo StoreManager\Edit.cshtml . Adicione a seguinte linha logo após a tag legenda.

@Html.HiddenFor(model => model.AlbumId)

O código a seguir mostra o contexto dessa alteração:

@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. -->

}

O AlbumId é necessário para fazer alterações em um registro de álbum.

Pressione CTRL+F5 para executar o aplicativo. Selecione o link Admin e, em seguida, selecione o link Criar novo para criar um novo álbum. Verifique se as informações do álbum foram salvas. Edite um álbum e verifique se as alterações feitas são mantidas.

O esquema do álbum

O StoreManager controlador criado pelo mecanismo de scaffolding MVC permite acesso CRUD (Criar, Ler, Atualizar, Excluir) aos álbuns no banco de dados da loja de música. O esquema para informações do álbum é mostrado abaixo:

Imagem do esquema do álbum

A Albums tabela não armazena o gênero e a descrição do álbum, ela armazena uma chave estrangeira para a Genres tabela. A Genres tabela contém o nome e a descrição do gênero. Da mesma forma, a Albums tabela não contém o nome dos artistas do álbum, mas uma chave estrangeira para a Artists tabela. A Artists tabela contém o nome do artista. Se você examinar os dados na Albums tabela, poderá ver que cada linha contém uma chave estrangeira para a Genres tabela e uma chave estrangeira para a Artists tabela. A imagem abaixo mostra alguns dados da Albums tabela.

Imagem de alguns dados da tabela Álbuns

A tag HTML Select

O elemento HTML <select> (criado pelo auxiliar HTML DropDownList ) é usado para exibir uma lista completa de valores (como a lista de gêneros). Para formulários de edição, quando o valor atual é conhecido, a lista de seleção pode exibir o valor atual. Vimos isso anteriormente quando definimos o valor selecionado como Comédia. A lista de seleção é ideal para exibir dados de categoria ou chave estrangeira. O <select> elemento da chave estrangeira Genre exibe a lista de possíveis nomes de gênero, mas quando você salva o formulário, a propriedade Genre é atualizada com o valor da chave estrangeira Genre, não com o nome do gênero exibido. Na imagem abaixo, o gênero selecionado é Disco e a artista é Donna Summer.

Imagem do gênero selecionado Disco

Examinando o código de scaffolded do MVC ASP.NET

Abra o arquivo Controllers\StoreManagerController.cs e localize o HTTP GET Create método.

public ActionResult Create()

{

    ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name");

    ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name");

    return View();

}

O Create método adiciona dois objetos SelectList ao ViewBag, um para conter as informações de gênero e outro para conter as informações do artista. A sobrecarga do construtor SelectList usada acima usa três argumentos:

public SelectList(

    IEnumerable items,

    string dataValueField,

    string dataTextField

)
  1. items: um IEnumerable que contém os itens na lista. No exemplo acima, a lista de gêneros retornada por db.Genres.
  2. dataValueField: o nome da propriedade na lista IEnumerable que contém o valor da chave. No exemplo acima, GenreId e ArtistId.
  3. dataTextField: o nome da propriedade na lista IEnumerable que contém as informações a serem exibidas. Tanto na tabela de artistas quanto na de gênero, o name campo é usado.

Abra o arquivo Views\StoreManager\Create.cshtml e examine a Html.DropDownList marcação auxiliar para o campo de gênero.

@model MvcMusicStore.Models.Album

@*        Markup removed for clarity.*@

@Html.DropDownList("GenreId", String.Empty)

A primeira linha mostra que a visualização de criação usa um Album modelo. Create No método mostrado acima, nenhum modelo foi passado, portanto, a exibição obtém um modelo nuloAlbum. Neste ponto, estamos criando um novo álbum, então não temos dados Album para ele.

A sobrecarga Html.DropDownList mostrada acima usa o nome do campo a ser associado ao modelo. Ele também usa esse nome para procurar um objeto ViewBag que contém um objeto SelectList . Usando essa sobrecarga, você deve nomear o objeto GenreIdViewBag SelectList . O segundo parâmetro (String.Empty) é o texto a ser exibido quando nenhum item for selecionado. Isso é exatamente o que queremos ao criar um novo álbum. Se você removeu o segundo parâmetro e usou o seguinte código:

@Html.DropDownList("GenreId")

A lista de seleção seria padronizada para o primeiro elemento, ou Rock em nosso exemplo.

Imagem do primeiro elemento padrão

Examinando o HTTP POST Create método.

//

// 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);

}

Essa sobrecarga do Create método usa um album objeto, criado pelo sistema de associação de modelo MVC ASP.NET dos valores de formulário postados. Quando você envia um novo álbum, se o estado do modelo for válido e não houver erros de banco de dados, o novo álbum será adicionado ao banco de dados. A imagem a seguir mostra a criação de um novo álbum.

Imagem mostrando a criação de um novo álbum

Você pode usar a ferramenta fiddler para examinar os valores de formulário postados que ASP.NET associação de modelo MVC usa para criar o objeto de álbum.

Imagem da ferramenta Fiddler.

Refatoração da criação de ViewBag SelectList

Edit Os métodos e o HTTP POST Create método têm código idêntico para configurar o SelectList no ViewBag. No espírito do DRY, vamos refatorar esse código. Usaremos esse código refatorado posteriormente.

Crie um novo método para adicionar um gênero e um artista SelectList ao 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);

}

Substitua as duas linhas que definem o ViewBag em cada um dos Create métodos e Edit por uma chamada para o SetGenreArtistViewBag método. O código completo é mostrado abaixo.

//

// 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);

}

Crie um novo álbum e edite-o para verificar se as alterações funcionam.

Passando explicitamente o SelectList para o DropDownList

As exibições de criação e edição criadas pelo scaffolding MVC ASP.NET usam a seguinte sobrecarga 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"

)

A DropDownList marcação para a visualização de criação é mostrada abaixo.

@Html.DropDownList("GenreId", String.Empty)

Como a ViewBag propriedade do SelectList é nomeada GenreId, o auxiliar DropDownList usará oGenreId SelectList no ViewBag. Na sobrecarga DropDownList a seguir, o SelectList é explicitamente passado.

public static MvcHtmlString DropDownList(

    this HtmlHelper htmlHelper,

    string name,            // The name of the ViewModel property to bind.

    IEnumerable selectList  // The SelectList

)

Abra o arquivo Views\StoreManager\Edit.cshtml e altere a chamada DropDownList para passar explicitamente o SelectList, usando a sobrecarga acima. Faça isso para a categoria Gênero. O código completo é mostrado abaixo:

@Html.DropDownList("GenreId", ViewBag.GenreId as SelectList)

Execute o aplicativo e clique no link Admin , navegue até um álbum do Jazz e selecione o link Editar .

Imagem da seleção do álbum de Jazz para editar

Em vez de mostrar Jazz como o gênero selecionado no momento, Rock é exibido. Quando o argumento de cadeia de caracteres (a propriedade a ser associada) e o objeto SelectList têm o mesmo nome, o valor selecionado não é usado. Quando não há nenhum valor selecionado fornecido, os navegadores usam como padrão o primeiro elemento no SelectList (que é Rock no exemplo acima). Essa é uma limitação conhecida do auxiliar DropDownList .

Abra o arquivo Controllers\StoreManagerController.cs e altere os nomes dos objetos SelectList para Genres e Artists. O código completo é mostrado abaixo:

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);

}

Os nomes Gêneros e Artistas são nomes melhores para as categorias, pois contêm mais do que apenas o ID de cada categoria. A refatoração que fizemos anteriormente valeu a pena. Em vez de alterar o ViewBag em quatro métodos, nossas alterações foram isoladas no SetGenreArtistViewBag método.

Altere a chamada DropDownList nos modos de exibição de criação e edição para usar os novos nomes SelectList . A nova marcação para a visualização de edição é mostrada abaixo:

<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>

O modo de exibição Criar requer uma cadeia de caracteres vazia para impedir que o primeiro item na SelectList seja exibido.

<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>

Crie um novo álbum e edite-o para verificar se as alterações funcionam. Teste o código de edição selecionando um álbum com um gênero diferente de Rock.

Usando um modelo de exibição com o auxiliar DropDownList

Crie uma nova classe na pasta ViewModels chamada AlbumSelectListViewModel. Substitua o AlbumSelectListViewModel código na classe pelo seguinte:

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);

        }

    }

}

O AlbumSelectListViewModel construtor pega um álbum, uma lista de artistas e gêneros e cria um objeto contendo o álbum e um SelectList para gêneros e artistas.

Construa o projeto para que ele AlbumSelectListViewModel esteja disponível quando criarmos uma exibição na próxima etapa.

Adicione um EditVM método ao StoreManagerController. O código completo é mostrado abaixo.

//

// 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);

}

Clique com o botão direito do mouse AlbumSelectListViewModel, selecione Resolver e, em seguida , usando MvcMusicStore.ViewModels;.

Seleção de imagem resolver

Como alternativa, você pode adicionar a seguinte instrução using:

using MvcMusicStore.ViewModels;

Clique com o botão direito do mouse EditVM e selecione Adicionar exibição. Use as opções mostradas abaixo.

Imagem mostrando a caixa de diálogo Adicionar vista

Selecione Adicionar e substitua o conteúdo do arquivo Views\StoreManager\EditVM.cshtml pelo seguinte:

@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>

A EditVM marcação é muito semelhante à marcação original Edit , com as seguintes exceções.

  • As propriedades do Edit modelo na vista são do formato model.property(por exemplo, model.Title ). As propriedades do EditVm modelo na vista são do formato model.Album.property(por exemplo, model.Album.Title). Isso ocorre porque a EditVM exibição recebe um contêiner para um Album, não um Album como na Edit exibição.
  • O segundo parâmetro DropDownList vem do modelo de exibição, não do ViewBag.
  • O auxiliar BeginForm no EditVM modo de exibição posta explicitamente de volta para o Edit método de ação. Ao postar de volta na Edit ação, não precisamos escrever uma HTTP POST EditVM ação e podemos reutilizá-la HTTP POST Edit .

Execute o aplicativo e edite um álbum. Altere o URL para usar EditVM. Altere um campo e clique no botão Salvar para verificar se o código está funcionando.

Imagem com U R L mude para Editar V M

Qual abordagem você deve usar?

Todas as três abordagens mostradas são aceitáveis. Muitos desenvolvedores preferem passar explicitamente o SelectList para o DropDownList usando o ViewBag. Essa abordagem tem a vantagem adicional de oferecer a flexibilidade de usar um nome mais apropriado para a coleção. A única ressalva é que você não pode nomear o ViewBag SelectList objeto com o mesmo nome que a propriedade model.

Alguns desenvolvedores preferem a abordagem ViewModel. Outros consideram a marcação mais detalhada e o HTML gerado da abordagem ViewModel uma desvantagem.

Nesta seção, aprendemos três abordagens para usar o DropDownList com dados de categoria. Na próxima seção, mostraremos como adicionar uma nova categoria.