Problem setting the default value for ASP.NET MVC DropDownList
Recently one of the readers reported a problem that the DropDownList helper
is not working as expected. The problem was resolved by slightly tweaking the
code but since it sounded like a tricky thing that many beginners would stumble
upon I am writing this post. Let's see what the problem is, its cause and
finally the little tweak that I mentioned earlier.
Suppose that you have a View to enter Customer details such as CustomerID,
CompanyName, ContactName and Country. The Country value is to be presented to
the user as a DropDownList so that he can pick one value from the list. When the
view is displayed the first time some default Country value must be selected in
the DropDownList. This default value is not same as the first entry from the
DropDownList. It is determined based on some condition (say language setting or
some value saved in the database) and is to be assigned through code.
To accomplish this task the developer wrote this code:
public ActionResult New()
{
var countryQuery = (from c in db.Customers
orderby c.Country ascending
select c.Country).Distinct();
List<SelectListItem> countryList = new List<SelectListItem>();
string defaultCountry = "USA";
foreach(var item in countryQuery)
{
countryList.Add(new SelectListItem() {
Text = item,
Value = item,
Selected=(item == defaultCountry ? true : false) });
}
ViewBag.Country = countryList;
return View();
}
The above code shows New() action method of a controller. The code shown
above selects distinct countries from the Customers table (Northwind database).
It then declares a generic List of SelectListItem objects. A SelectListItem
class represents one entry of the DropDownList. A foreach loop iterates through
the available countries and adds the corresponding SelectListItem objects to the
List. The Text and Value properties of the SelectListItem indicate the text of
an item and the value of an item respectively. The Selected boolean property
controls whether an item should be selected or not. The Selected property is set
by checking the current country with the one stored in defaultCountry variable
(USA in this case). Once a List of SelectListItem is created the List is passed
to the view in a ViewBag property - Country.
The New view displayed by the New() action method is shown below:
@model DropDownListDemo.Models.Customer
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Edit</title>
</head>
<body>
@using(Html.BeginForm("Insert","Home",FormMethod.Post))
{
@Html.TextBox("CustomerID")
@Html.TextBox("CompanyName")
@Html.TextBox("ContactName")
@Html.DropDownList("Country", ViewBag.Country as List<SelectListItem>)
<input type="submit" value="Insert" />
}
</body>
</html>
The New view is quite straightforward and uses HTML helpers such as TextBox
and DropDownList to render the respective form fields. The form is submitted to
the Insert() action method of the Home controller. Notice the DropDownList
markup. The DropDownList markup renders a <select> element with name equal to
Country and Country ViewBag property supplies the List of SelectListItem objects
to it.
So far so good. At this point you might expect that running the New view will
render a DropDownList with USA selected in it. Right? Unfortunately, the answer
is No. The DropDownList doesn't select USA at all. Instead the first item from
the DropDownList gets selected.
What's the problem? The problem is the ViewBag property you created to pass
the List to the view. If you observe the above code carefully, you will realize
that the ViewBag property name and DropDownList name both are set to Country.
This creates the problem. While rendering the DropDownList, MVC looks for a
ViewData (and hence ViewBag) variable whose name is same as the DropDownLIst
being rendered. If such a ViewData variable is found, value of that variable is
used to determine the selected value of the DropDownList. If no compatible value
is found nothing is set as the default. So, in the above code MVC is trying to
assign the value of Country ViewBag property to Country DropDownList and they
don't match. That's why USA is not selected in the DropDownList in spite of
setting it as a default for one of the SelectListItem.
To rectify the problem simply change the name of the ViewBag property to
something different. For example, the following code sets it to Countries
instead of Country.
public ActionResult New()
{
...
ViewBag.Countries = countryList;
return View();
}
Also change the view markup as follows:
@Html.DropDownList("Country", ViewBag.Countries as List<SelectListItem>)
If you run the view again, you will find that this time USA gets selected in
the DropDownList. Just to confirm what I explained earlier add another ViewBag
property as follows:
public ActionResult New()
{
...
ViewBag.Countries = countryList;
ViewBag.Country = "UK";
return View();
}
As you can see, two ViewBag properties are set - Countries and Country. The
Countries property points to the List of SelectListItem objects whereas Country
property holds a value of UK. If you run the view you will find that the
DropDownList displays UK as its default value. Thus default of USA is overridden
by ViewBag property with the same name as that of the DropDownList.
To summarize, whenever you wish to pass data for a DropDownList through
ViewData or ViewBag, ensure that ViewData / ViewBag property names don't
conflict with the form field names. In most of the cases you will need to change
the ViewBag property names to something different because form field names are
necessary for model binding to work correctly.