Html.DropDownList is used when you need to create a view that has a dropdown that allows you to select one of several possible values for one of your model’s properties.
For example you have a Model that describes a Employee. One Employee has one Manager and in the Employee’s Edit view you want to display a drop down with all the possible values for Manager.
So the goal is to have something that looks like this:
Here’re the models that you can use to try to build this canonical example yourself:
First, the employee model:
public class Employee { public int Id { get; set; } public string Name { get; set; } public int ManagerId { get; set; } }
In the view I added the managers as a list of person, here’s the Person class:
public class Person { public int Id { get; set; } public string Name { get; set; } }
Now the controller (just the edit actions):
public ActionResult Edit(int id) { //Fetch employee Employee employee = new Employee(); employee.Id = id; employee.ManagerId = 4; //Fetch managers List<Person> managers = new List<Person>() { new Person() { Id = 1, Name = "John"}, new Person() { Id = 2, Name = "Richard"}, new Person() { Id = 3, Name = "Peter"}, new Person() { Id = 4, Name = "Paul"} }; ViewBag.ManagerId = new SelectList(managers, "Id", "Name", employee.ManagerId); return View(employee); } [HttpPost] public ActionResult Edit(Employee employee) { //you should find your model binded employee here }
For the sake of simplicity all values are hardcoded.
Important things to note about the controller’s edit action:
- The adding of the ManagerId to the ViewBag as a SelectList containing all the managers, the value field (Id), the description field (“Name”) and the selected value (employee.ManagerId)
- A SelectList represents a list where one of the items can be selected. It allows you to specify which is the value that should be returned, the desciption, optional text and which item is selected by default (see http://msdn.microsoft.com/en-us/library/system.web.mvc.selectlist.selectlist). It is a convenient way to set the values when using the html helper DropDownList
- The fact that it was added to the ViewBag as ManagerId is also important. ManagerId is the name of the property that we want to set in the Model (Employee)
If you use scaffolding to add the Edit view by right clicking on View(employee) and selecting Add View:
You’ll be disappointed to find out that the view will display a textbox where you can type the Id of the Manager (at least if you are using Visual Studio 2012 RC and an MVC4 project template, although I didn’t test I think this used to work in MVC3). Not ideal. But it is easy to fix, but first let me show you what you’ll get when you use scaffolding:
<div class="editor-label"> @Html.LabelFor(model => model.ManagerId) </div> <div class="editor-field"> @Html.EditorFor(model => model.ManagerId) @Html.ValidationMessageFor(model => model.ManagerId) </div>
Not what we want, but it is very easy to change, just change the EditorFor( mode=> model.ManagerId) to @Html.DropDownList(“ManagerId”, string.Empty) and you are done.
There are a few conventions here that make this to work:
- The Html.DropDownList will look in the ViewData (ViewBag) for an entry with key “ManagerId” that contains a SelectList (an IEnumerable<SelectListItem> would work as well)
- It will generate an html select element with a name attribute value of “ManagerId”. This is important because MVC’s model binder will look inside the post data for elements with the same name as the properties in the model, in this case Employee. Because Employee has a ManagerId property and the post data contains an element with that name the model binder will fill it as we intend.
- The string.Empty is a value that is added to the beginning of the list. Think of it as representing that the employee does not have a manager.
If you think this is too much convention and it makes it difficult to understand what is going on you may think it is better to use one of the overloads of Html.DropDownList. For example, you might be tempted to do this:
@Html.DropDownList(“ManagerId”, (SelectList)ViewBag.ManagerId, string.Empty)
Remember that ManagerId in the ViewBag contains a SelectList the was initialized with all the managers, one optional value and a selected value. The selected value will not work (won’t be selected) if your DropDownList’s name is the same as the model property (see http://stackoverflow.com/questions/624828/asp-net-mvc-html-dropdownlist-selectedvalue). I don’t really know why this happens (if you find out please let me know).
However if you set the SelectList in the ViewBag with a different name than your model’s property the select that is rendered will behave as expected and the model binding will still work:
public ActionResult Edit(int id) { //Fetch employee Employee employee = new Employee(); employee.Id = id; employee.ManagerId = 4; //Fetch managers List<Person> managers = new List<Person>() { new Person() { Id = 1, Name = "John"}, new Person() { Id = 2, Name = "Richard"}, new Person() { Id = 3, Name = "Peter"}, new Person() { Id = 4, Name = "Paul"} }; ViewBag.Managers = new SelectList(managers, "Id", "Name", employee.ManagerId); return View(employee); }
<div class="editor-label"> @Html.LabelFor(model => model.ManagerId) </div> <div class="editor-field"> @Html.DropDownList("ManagerId", (SelectList)ViewBag.Managers, string.Empty) @Html.ValidationMessageFor(model => model.ManagerId) </div>
Or alternatively you can use DropDownListFor:
<div class="editor-label"> @Html.LabelFor(model => model.ManagerId) </div> <div class="editor-field"> @Html.DropDownListFor(model => model.ManagerId, (SelectList)ViewBag.Managers) @Html.ValidationMessageFor(model => model.ManagerId) </div>