Drop down lists in ASP.NET MVC
Full source code is a attached here.
This post shows two methods of implementing drop down lists in ASP.NET MVC 4. The code for data access and the general layout of the application should not be considered suitable for anything other than pedagogical purposes.
The provided source code uses entity framework and requires a local database to be running, see the web.config for naming.
The main components of the application are Vehicle
and TyreType
classes, and controllers for each.
The controllers have the standard Index/Create/Edit/Details/Delete
views and actions.
The VehicleController
shows two ways of providing a TyreType
drop down list, one using the ViewBag
and the other using a VehicleViewModel
.
The TyreType
is as follows.
1using System.ComponentModel.DataAnnotations;
2
3namespace Automobile.Models
4{
5 public class TyreType
6 {
7 public int TyreTypeID { get; set; }
8 [Required]
9 public string Name { get; set; }
10 [Required]
11 public string Material { get; set; }
12 }
13}
And the Vehicle
looks like this.
1using System.ComponentModel.DataAnnotations;
2
3namespace Automobile.Models
4{
5 public class Vehicle
6 {
7 public int VehicleID { get; set; }
8 [Required]
9 public string Name { get; set; }
10 public string Description { get; set; }
11 public double MSRP { get; set; }
12 [Required]
13 public int TyreTypeID { get; set; }
14 public virtual TyreType TypeType { get; set; }
15 }
16}
The TyreTypeController
is a standard controller with text boxes for entering information on the Edit
and Create
views.
The VehicleController
has a drop down list filled with available tyre types as found in the database.
Using the ViewBag
The first method of filling the drop down list is to use the ViewBag
.
In the [HttpPost]Edit
method we have -
ViewBag.TyreTypeID = new SelectList(db.TyreType, "TyreTypeID", "Name", vehicle.TyreTypeID);
I’ll explain in detail what this line of code is doing -
ViewBag.TyreTypeID
– is referencing the ViewBag
and dynamically adding an entry called TyreTypeID
.
new SelectList(db.TyreType, "TyreTypeID", "Name", vehicle.TyreTypeID)
– is doing five things -
1. `new SelectList` - creates a new `SelectList`(this is what drop down lists use)
2. `db.TyreType` - passing in the `TypeTypes` from the database
3. `"TyreTypeID"` - specifying that this public property of `TyreType` will be used for `dataValueField`
4. `"Name"` - specifying that this public property of `TyreType` will be used for `dataTextField`
5. `vehicle.TyreTypeID` – specifying the selected value in the drop down list, note that it is the same property as the `dataValueField`
Inside the TyreType
Edit view we have -
1@model Automobile.Models.Vehicle
2…snip…
3 <div class="editor-label">
4 @Html.LabelFor(model => model.TyreTypeID, "Type Type")
5 </div>
6 <div class="editor-field">
7 @Html.DropDownList("TyreTypeID","--Select a tyre type--")
8 @Html.ValidationMessageFor(model => model.TyreTypeID)
9 </div>
In this example the "TyreTypeID"
specifies both the source of items for the drop down list and the destination property for the selected value that will be sent back to the controller. In the example, the Vehicle
is sent to the Edit
action, so the Vehicle
. TyreTypeID
is set with the selected value.
I found this very confusing at first because I wanted to change the name of the ViewBag
property for storing the drop down list items, so my selected value was being lost. MVC is doing a lot by convention over coding, if you fight it you will have to learn many rules.
I’m not a big fan of this approach, too many things are happening without my explicit control and if forces me to use the same name for a list of items (the SelectList
in the ViewBag
) and the selected item (an int
).
Using a ViewModel
One alternative is to create a ViewModel
for the Vehicle
and the values for the drop down list. Note again, I’m not advocating this methodology for data access for production code.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Web.Mvc;
5using Automobile.DataAccess;
6using Automobile.Models;
7
8namespace Automobile.ViewModels
9{
10 public class VehicleViewModel
11 {
12 private AutomobileContext db = new AutomobileContext();
13 public IEnumerable<SelectListItem> TyreTypes { get; set; }
14 public Vehicle Vehicle { get; set; }
15 public VehicleViewModel(Vehicle vehicle)
16 {
17 Vehicle = vehicle;
18 TyreTypes = PopulateTyreTypes();
19 }
20 private IEnumerable<SelectListItem> PopulateTyreTypes()
21 {
22 var tyreTypesQuery = db.TyreType.OrderBy(t => t.Name);
23 return new SelectList(tyreTypesQuery,"TyreTypeID","Name");
24 }
25 }
26}
Inside the VehicleController
I have
1public ActionResult Create()
2{
3 //we're going to use the view model to send the tyre types to the view here.
4 var vehicleViewModel = new VehicleViewModel(new Vehicle());
5 return View(vehicleViewModel);
6}
7
8[HttpPost]
9[ValidateAntiForgeryToken]
10public ActionResult Create(Vehicle vehicle)
11{
12 if (ModelState.IsValid)
13 {
14 db.Vehicle.Add(vehicle);
15 db.SaveChanges();
16 return RedirectToAction("Index");
17 }
18 var vehicleViewModel = new VehicleViewModel(vehicle);
19 return View(vehicleViewModel);
20}
And the view has
1@model Automobile.ViewModels.VehicleViewModel
2…snip…
3<div class="editor-label">
4 @Html.LabelFor(model => model.Vehicle.TyreTypeID, "Tyre Type")
5</div>
6<div class="editor-field">
7 @Html.DropDownListFor(model => model.Vehicle.TyreTypeID, Model.TyreTypes, "--Select a tyre type--" )
8 @Html.ValidationMessageFor(model => model.Vehicle.TyreTypeID)
9</div>
@Html.DropDownListFor(model => model.Vehicle.TyreTypeID, Model.TyreTypes, "--Select a tyre type--" )
is doing three things –
1. `model => model.Vehicle.TyreTypeID` – specifies where the selected value from the DDL is stored
2. `Model.TyreTypes` – is the source of SelectListItems for the DDL
3. `"--Select a tyre type--"` – is a default value
This is the approach I prefer.
Full source code is a attached here.