ASP.NET MVC Html.DropDownList Not Showing Selected Value

I’ll have to admit, this is one of the only unfortunate nuances that I’ve come across in the ASP.NET MVC framework. 

Problem:   You’re using the Html.DropDownList, it’s name property is the same as your model’s property name, you pass a SelectList with you selected value, but in the UI it’s not showing your expected selection.  The reason behind this problem is that ASP.NET MVC first looks for a match between the name of the drop down and a property on the model. If there’s a match, the selected value of the SelectList is overridden and no value is selected.  This can be annoying.  Here is our example, which is as simple as I can get it without loosing the meaning.

// models
public class CarRegistration {
public string CountryCode {get;set;} // iso 2 char code
}
public class Car {
public CarRegistration Registration {get;set;}
public string Color {get;set;}
}

public CarsController : Controller {
[HttpGet]
public ActionResult Edit(string vin) {
Car car = carsDao.FindByVin(vin);
ViewData["countries"] = new SelectList(
contriesDao.List(), // list of countries for select list
"Code", // select value attribute
"CountryName", // select text display
car.Registration.CountryCode); // selected value
return View(car);
}
}
 
In your view you have the following Html.DropDownList helper rendering the countries list.  Notice that the name for the drop down list is “Registration.CountryCode”.  The reason for doing this is so when form is posted back, it will bind to the car instances property.  This part is handy, but what you don’t expected is that when the edit view is displayed the value that is stored in our car’s registration country is not selected in the drop down.
 
<%= Html.DropDownList("Registration.CountryCode", (IEnumerable<SelectListItem>)ViewData["countries"]) %>

Lets pretend our registration country for some car is DE, or Germany.  Your SelectList will have a internal list of SeletListItem’s that should produce the following in the rendered view.  Instead, of rendering what is show below, you get a select list of countries without any options that have the selected attribute set.
 
<select id="Registration_CountryCode" name="Registration.CountryCode">
...
<option value="GE">Georgia</option>
<option value="DE" selected="selected">Germany</option>
<option value="GH">Ghana</option>
...
</select>

Solution:  The solution to this issue lies within the name of the drop down list.  Changing the name to not match the model’s property name will correctly render the drop down with the selected value.  So change our view to the following corrects the problem.

<%= Html.DropDownList("countryCode", (IEnumerable<SelectListItem>)ViewData["countries"]) %>

The downside to this is that you cannot rely on model binding and have to pick this value out manually.

...
public ActionResult Edit(Car car) {
// get registration country
car.Registration.CountryCode = Request.Form["countryCode"];
}

Alternate Solution:  Ok, I had to amend this post because after looking at the source code for the MVC framework I noticed something that wasn’t quite intuitive.  There actually is a way to keep the model binding and have the correct select list and selected value.  The trick is you must add the selected list to the ViewData with the key name of you property and pass null to the Html.DropDownList helper (or use the DropDownListFor helper).  According to the example above here is how it is done:

public CarsController : Controller {
[HttpGet] public ActionResult Edit(string vin) {
Car car = carsDao.FindByVin(vin);
ViewData["Registration.CountryCode"] = new SelectList( // notice the view data key name!
contriesDao.List(), // list of countries for select list
"Code", // select value attribute
"CountryName", // select text display
car.Registration.CountryCode); // selected value
return View(car);
}
}

Our view now will have this:

<%= Html.DropDownList("Registration.CountryCode") %>

OR

<%= Html.DroopDownListFor(m => m.Registration.CountryCode)%>

The reason this works is because the helper extension for the DropDownList checks the ViewData collection for a key named “Registration.CountryCode” and casts it to IEnumerable<SelectListItem>.  Since you already stuffed this in the ViewData, it finds your existing list with the correctly selected value and inserts into the view.  In addition, you model binding will work now!

8 comments:

Petr said...

I had exactly the same problem with ASP.NET MVC 3. DropDown always had first value in its items list selected, completely ignoring Selected property of SelectListItem-s.

I changed the name of DropDown and it has helped, so thank you for the idea.

Yasith said...

This was really helpful and I was stuck and thinking few days to find out a solution. This post is the one and only post I found in the web to help me out in this scenario.

Anonymous said...

This was of great help! thank you very very much!!

Anonymous said...

html helper DropDownListFor does not have an overload that takes just one argument.

Please revise:
<%= Html.DroopDownListFor(m => m.Registration.CountryCode)%>
as well as:
<%= Html.DropDownList("Registration.CountryCode") %>

Anonymous said...

You can use the list of elements using ViegBag.ElementList as IEnumerable insted the SelectList object.
... ViewBag.ElementList = contriesDao.List(); ...
And in the view
... @Html.DropDownListFor(m=>m.Registration.CountryCode,@ViewBag.ElementList as IEnumerable<SelectListItem>)

Anonymous said...

Thanks, had been searching the net for about 2 hours to find a solution to this problem. All now working! Thank you.

Anoop said...

Thank you. It helped me!!

Franklin Ledezma Ch said...

This was very helpfull thank you very much for sharing.