Get The Drop On ASP.NET MVC DropDownLists

DropDownLists in ASP.NET MVC seem to cause a fair amount of confusion to developers fresh from Web Forms World. This article looks to provide you with all (well, most) of what you need to know to get your DropDownLists working in ASP.NET MVC.

DropDownList, ComboBox, call it what you like, but it always renders as an html select element. It has an opening <select> tag, and a closing </select> tag. In between, each "ListItem" is housed within an <option> tag. Optionally, they may be subdivided into <optgroup> elements for logical separation of related options. If you provide a value attribute to an option, that is the value that gets posted back when a form housing the select element is submitted. If you omit the value attribute, the text value of the option gets posted back.

At its simplest, for example if you have a static list of items that needs to appear in a DropDown, you can simply put them in your View as html:


<select name="year">
  <option>2010</option>
  <option>2011</option>

  <option>2012</option>
  <option>2013</option>
  <option>2014</option>

  <option>2015</option>
</select>

Or, if the list is a little more dynamic, say if you need to ensure that the starting year is incremented by 1 each New Year's Day:

[WebForms]

<select name="year">

  <option><%= DateTime.Now.Year %></option>
  <option><%= DateTime.Now.AddYears(1).Year %></option>

  <option><%= DateTime.Now.AddYears(2).Year %></option>
  <option><%= DateTime.Now.AddYears(3).Year %></option>

  <option><%= DateTime.Now.AddYears(4).Year %></option>
  <option><%= DateTime.Now.AddYears(5).Year %></option>

</select>

[Razor]

<select name="year">

  <option>@DateTime.Now.Year</option>
  <option>@DateTime.Now.AddYears(1).Year</option>

  <option>@DateTime.Now.AddYears(2).Year</option>
  <option>@DateTime.Now.AddYears(3).Year</option>
  <option>@DateTime.Now.AddYears(4).Year</option>
  <option>@DateTime.Now.AddYears(5).Year</option>

</select>

Or even:

[WebForms]  

<select name="year">
  <% for (var i = 0; i < 6; i++){%>

    <option><%= DateTime.Now.AddYears(i).Year %></option>
  <%}%>
</select>

[Razor]  

<select name="year">
  @for (var i = 0; i < 6; i++){

    <option>@(DateTime.Now.AddYears(i).Year)</option>
  }
</select>

All of the above will render exactly the same html and end result:

If your data comes from a database, you will more likely use one of the 8 overloads of the Html.DropDownList() extension method to create your DropDown. I won't cover all overloads, but it is worth looking at the main ones. The first one - public static string DropDownList(this HtmlHelper htmlHelper, string name) - simply accepts a string. Now the documentation currently says that the string should be the name of the form field, which isn't particularly helpful. In fact, not only does it provide the resulting select element with a name and an id, but it also acts as the look-up for an item in the ViewBag having the same dynamic property as the string provided. This ViewBag property is then bound to the helper to create the <option> items. Consequently, the ViewBag property must be a collection of SelectListItems. Here's how to get the Categories from the Northwind sample database using LINQ to SQL to pass to a DropDownList using the first overload:


public ActionResult Index()
{
  var db = new NorthwindEntities();
  IEnumerable<SelectListItem> items = db.Categories
    .Select(c => new SelectListItem
                   {
                     Value = c.CategoryID.ToString(), 
                     Text = c.CategoryName
                   });
  ViewBag.CategoryID = items;
  return View();
}


Notice that each SelectListItem must have a Value and a Text property assigned. These are bound at run-time to the value attribute of the option elements and the actual text value for the option. Notice also the odd name given to the ViewBag dynamic property "CategoryID". The reason for this is that the CategoryID is the value that will be passed when the form is submitted, so it makes sense to name it like this. In the View, the overload is used:

[WebForms]

<%= Html.DropDownList("CategoryID") %>

[Razor]

@Html.DropDownList("CategoryID")

And that's all that's needed to render the following HTML:


<select id="CategoryID" name="CategoryID">
  <option value="1">Beverages</option>

  <option value="2">Condiments</option>
  <option value="3">Confections</option>
  <option value="4">Dairy Products</option>

  <option value="5">Grains/Cereals</option>
  <option value="6">Meat/Poultry</option>
  <option value="7">Produce</option>

  <option value="8">Seafood</option>
</select>

The second overload - public static string DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList) - is one you quite often see in examples. With this overload, you can return an IEnumerable<SelectListItem> collection or a SelectList object. We'll have a look at the View first, before seeing two methods of populating the ViewData with alternative objects:

[WebForms]  

<%= Html.DropDownList("CategoryID", (IEnumerable<SelectListItem>) ViewBag.Categories) %>


[Razor]  

@Html.DropDownList("CategoryID", (IEnumerable<SelectListItem>) ViewBag.Categories)


The first item to go into ViewBag will be the IEnumerable<SelectListItem> object. The code is pretty well identical to the previous example:


public ActionResult Index()
{
  var db = new NorthwindDataContext();
  IEnumerable<SelectListItem> items = db.Categories
    .Select(c => new SelectListItem
                   {
                     Value = c.CategoryID.ToString(),
                     Text = c.CategoryName
                   });
  ViewBag.Categories = items;
  return View();
}


The second passes a SelectList object to ViewBag:


public ActionResult Index()
{
  var db = new NorthwindDataContext();
  var query = db.Categories.Select(c => new { c.CategoryID, c.CategoryName });
  ViewBag.Categories = new SelectList(query.AsEnumerable(), "CategoryID", "CategoryName");
  return View();
}


Using a SelectList is slightly tidier in the Controller, and arguably in the View. The SelectList constructor has a couple of overloads which accepts an object representing the selected value:


public ActionResult Index()
{
  var db = new NorthwindDataContext();
  var query = db.Categories.Select(c => new { c.CategoryID, c.CategoryName });
  ViewBag.CategoryId = new SelectList(query.AsEnumerable(), "CategoryID", "CategoryName", 3);
  return View();
}


The above will ensure that "Confections" is selected when the list is rendered:

Default Values

All of the examples so far show the first selectable option visible when the page loads. Most often, however, a default value is desirable, whether this is a blank value or a prompt to the user to "--Select One--" or similar. Another overload takes care of adding this - public static string DropDownList(this HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, string optionLabel).

[WebForms]
<%= Html.DropDownList("CategoryID", (SelectList) ViewBag.CategoryId, "--Select One--") %>

[Razor]
@Html.DropDownList("CategoryID", (SelectList) ViewBag.CategoryId, "--Select One--") 

CSS and HTML attributes

Four of the overloads accept parameters for applying HTML attributes to the DropDownList when it is rendered. Two of them accept IDictionary<string, object> while the other two take an object. The object is an anonymous type. The following examples will both render identical html, applying a css class selector and a client-side onchange() event:

[Webforms]  

<%= Html.DropDownList(
    "CategoryID", 
    (SelectList)ViewBag.CategoryId, 
    "--Select One--", 
    new Dictionary<string, object>

                      {
                         {"class", "myCssClass"}, 
                         {"onchange", "someFunction();"}
                      }) %>
                      
                      
<%= Html.DropDownList(
    "CategoryID", 
    (SelectList)ViewBag.CategoryId, 
    "--Select One--", 
    new{  //anonymous type
          @class = "myCssClass", 
          onchange = "someFunction();"
       }) %>

[Razor]  

@Html.DropDownList(
    "CategoryID", 
    (SelectList)ViewBag.CategoryId, 
    "--Select One--", 
    new Dictionary<string, object>

                      {
                         {"class", "myCssClass"}, 
                         {"onchange", "someFunction();"}
                      }) 
                      
                      
@Html.DropDownList(
    "CategoryID", 
    (SelectList)ViewBag.CategoryId, 
    "--Select One--", 
    new{ //anonymous type
          @class = "myCssClass", 
          onchange = "someFunction();"
       }) 

You should notice that the second version (the one using the anonymous type) has a property called "@class". This will render as a literal "class", but needs the @ sign in front of "class" because class is obviously a C# keyword. You might also wonder why there are two ways to add attributes. The second option, using the anonymous object is a lot cleaner and surely would be the sensible choice. However, for one thing, the HTML5 specification includes the ability to add custom attributes to your html mark-up. Each attribute must be prefixed with "data-". If you attempt to create a property in a C# object with a hyphen in its name, you will receive a compiler error. The Dictionary<string, object> approach will solve that problem.

Where's My AutoPostBack?

One of the most common questions from developers used to the Web Forms model concerns AutoPostBack for DropDownLists in MVC. In Web Forms, it's easy enough to select your DropDownList in design view, head over to the Properties panel in your IDE and set AutoPostBack to true, or to tick the Use AutoPostBack option on the control's smart tag. Quite often, since it is that easy, developers give little thought to what happens behind the scenes when AutoPostBack is used. In fact, an onchange attribute is added to the rendered DropDownList, which fires a javascript event handler, causing the form in which the DropDownList is housed to be submitted. This process has to be done manually within MVC. But it's quite simple. I'll show two ways of achieving this. One will use the most recent overload (above) which takes an object for htmlAttributes, and the other one will show how the same thing can be done using jQuery, unobtrusively. I haven't actually shown DropDownLists within a form element so far, but of course a DropDownList is useless outside of one. Here's the first alternative:

[WebForms]

<% using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "TheForm" })){%>

  <%= Html.DropDownList(
    "CategoryID", 
    (SelectList) ViewData["Categories"], 
    "--Select One--", 
    new{
          onchange = "document.getElementById('TheForm').submit();"
       })%>

<%}%> 

[Razor]

@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "TheForm" })){

  @Html.DropDownList(
    "CategoryID", 
    (SelectList) ViewData["Categories"], 
    "--Select One--", 
    new{
          onchange = "document.getElementById('TheForm').submit();"
       })
} 

And the second that uses jQuery:

<script type="text/javascript">
  $(function() {
    $("#CategoryID").change(function() {
      $('#TheForm').submit();
    });
  });

</script>
[WebForms]
<%using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "TheForm" })){%>

  <%=Html.DropDownList("CategoryID", (SelectList) ViewBag.CategoryId, "--Select One--") %>
<%}%> 
[Razor]
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "TheForm" })){

  @Html.DropDownList("CategoryID", (SelectList) ViewBag.CategoryId, "--Select One--") 
} 
ToolTips

Nothing in the existing set of HtmlHelpers for DropDownLists provides for adding tool tips to select list options at the moment. Tool tips are generated by adding a "title" attribute to each option in the list. Now, this could be achieved by creating your own extension methods that allow you to specify that each option element should have a title, and then apply a  value to the title attribute as each option is added to the select list. But that's a fair amount of work... Or, you could use jQuery to do this really easily:

<script type="text/javascript">

  $(function() {
  $("#CategoryID option").each(function() {
      $(this).attr({'title': $(this).html()});
    });
  });
</script>

Strongly Typed Helper

All of the examples so far have illustrated the use of the dynamic ViewBag collection to pass values from Controllers to Views. There are strongly typed Html Helpers for the DropDownList to cater for strongly typed views - with all that Intellisense support and compile-time checking. The following example shows a very simple ViewModel class called SelectViewModel:

using System.Collections.Generic;
using System.Web.Mvc;


public class SelectViewModel
{
    public string CategoryId { get; set; }
    public IEnumerable<SelectListItem> List { get; set; }
}

Here's a sample controller action that instantiates a SelectViewModel instance and passes it to the strongly type view:

public ActionResult Index()
{
    var db = new NorthwindDataContext();
    var query = db.Categories.Select(c => new SelectListItem
                                              {
                                                  Value = c.CategoryID.ToString(), 
                                                  Text = c.CategoryName,
                                                  Selected = c.CategoryID.Equals(3)
                                              });
    var model = new SelectViewModel
                    {
                        List = query.AsEnumerable()
                    };
    return View(model);
}

Notice how the selected item is identified as the IEnumerable collection of SelectListItems is built from the database query. Finally, in the (Razor) view, the dropdown is rendered with a default option added:

@model SelectViewModel

@Html.DropDownListFor(m => m.CategoryId, Model.List, "--Select One--")

 

Date Posted: Thursday, January 7, 2010 9:27 PM
Last Updated: Saturday, April 30, 2011 12:00 PM
Posted by: Mikesdotnetting
Total Views to date: 328718

31 Comments

Friday, January 8, 2010 4:22 AM - Mike Sharp

Hi, Mike, Could you provide a printable version of the article?
And my name is Mike too :)

Saturday, January 9, 2010 4:41 AM - ali62b

Hi Mike,
Can you post and example how to bind DropDown in Edit scenarios?
(The selected value should be the one that saved in DB already and can select another one when saving. I think this is more related to Model Binding but is very useful in dropdown related issues)
Thanks in advance.

Saturday, January 9, 2010 8:37 AM - Mike

@Mike

That's something I've been meaning to do. Watch this space

@ali62b

I was thinking about looking at that separately.

Monday, January 11, 2010 5:12 PM - Dave Hanna

Mike,
I experienced a significant amount of confusion between the Html.DropDownList(name), and the Html.DropDownList(name, selectList) overloads, that you might want to expound on. In the first, the name variable identifies the select list as well as the selected item. When going to the second overload (forced by my need to use HtmlAttribute parameter, which isn't available with just the "name" overload), I assumed that the select list was the same one named by the name parameter. But the name parameter really changes function in the second overload to be a string or string array specifying the selected items.

Monday, January 11, 2010 7:51 PM - Mike

@Dave

In the first overload (string name), there is no variable for selectlist passed in, so the source code looks for an item in ViewData that has the same key as the value of name, and checks to see if it can be cast as an IEnumerable. If there isn't a matching item, an exception is thrown. That's all it does really. There is no selected itme if you use that overload. You must use one of the overloads where you explicitly pass in the selected item's value.

All of the overloads that allow you to specify htmlAttributes require the selectList to be passed in explicitly. Download the source code for MVC and look at the SelectExtension.cs file.

Monday, January 18, 2010 3:26 PM - ali62b

Today I found a post which covers editing scenarios with DropDownList. here is the address :
http://odetocode.com/Blogs/scott/archive/2010/01/18/drop-down-lists-and-asp-net-mvc.aspx

However wanted to know if this is a preferred way in your opinion or not.

Wednesday, January 20, 2010 1:45 PM - Mike

@ali62b

Thanks for the link to Scott's article. My personal preference is for strongly typed views, so yes - I would prefer to work with DropDownLists in the way he describes. But as with all these things, it's up to the developer to make the right decision in the context of the project he/she is working on. It's not for me to dictate what is right or wrong for other people.

Unless of course they don't agree with me :o)

Sunday, March 14, 2010 4:06 PM - Mike J

Perhaps I am thick. Is the mechanism you described for choosing a default value the approach to take when editing and matching the the selection to the users previous choice?

Sunday, March 14, 2010 4:40 PM - Mike

@Mike J

The default value is the one you normally see as the first one. What you need is one of the overloads that accepts a value for the Selected value.

Wednesday, May 19, 2010 12:36 AM - Jason

Mike, Is there a way for the client to enter text into the ddl instead of just selecting from the static list?

Thursday, May 20, 2010 3:27 PM - Mike

@Jason

No. You are referring to something equivalent to a VB style Combo Box. In order to make one of those, you need to fiddle around with a Text box and a div, plus some CSS. There is no HTML Combo box as such.

Thursday, July 8, 2010 2:52 AM - Atta ur Rahman

Hi... thanx for the nice article
but i want something more , can you please add ,how can i retrive dropdown selected item in my action controller after form submit....

not just the selected value , but both selected text and value in my FormCollection or any other way....

Thursday, July 8, 2010 5:29 AM - Mike

@Atta

There is no such thing as "selected text" in MVC. You can either do a lookup on your database to get the text associated with the posted value, or you can combine whatever you have as your current value and text, and make that the value. Then you can parse each item out when the form is posted back.

Friday, July 23, 2010 7:49 AM - John

Is there someway to do combobox in MVC? We want to convert a webform app that has 15 comboxes in a row. When they are not dropped down, they are nicely narrow and fit. When we click one of them, it widens and shows the contents. How to do this in MVC? Can we use the Ajax combobox as we did in the webform app?

Friday, July 23, 2010 8:24 AM - Mike

@John,

Comboboxes are generally nothing more than a text box with an image to ape the dropdown botton, a div and some css and javascript. I tend to steer clear of ASP.NET AJAX stuff and look for jQuery alternatives. Google or Bing should help you.

Friday, January 21, 2011 7:58 PM - San

Thanks for the Article. It helped me a lot to understand.

Friday, May 6, 2011 6:31 AM - Gregg

Thank you very much...I was having trouble applying Html Attributes to a dropdown list but this article really helped!

Friday, April 27, 2012 3:11 PM - Araik

Thanks!

Monday, May 14, 2012 3:29 PM - Gift White

this is what
I was looking for :)

thank you

Sunday, May 20, 2012 9:13 AM - seyhan bakır

thank you..very usefull post

Sunday, May 27, 2012 1:33 PM - Jade Tibbitts

Thanks For This. After Pissing around for hours on end your article was the only one in which could help me. So Big Thanks

Wednesday, June 13, 2012 1:36 PM - Stanislava Pavlova

Hi :) I want to ask something... Is this working in MVC2? Couse i'm trying but it doesn't work... I want to know if it's my mistake :) Thanks in advance!

Thursday, June 14, 2012 9:52 PM - Mike

@Stanislava,

Yes, the code will work with MVC 2. You should post a question at http://forums.asp.net

Friday, August 31, 2012 8:32 AM - flytomylife

Really thanks a lot.

Thursday, September 13, 2012 5:27 PM - Dave Stuart

Great article! I'm gonna use this as a reference.

Wednesday, September 19, 2012 10:14 PM - Ahmet

really very useful. thanks for sharing ;)

Monday, April 29, 2013 2:37 PM - sam

you rock man...

Wednesday, July 17, 2013 4:02 AM - Jatinder Kumar

Very great article dear.This article help me alot very very helpful
Thank you very much.

Monday, October 7, 2013 1:34 PM - Upen

Thanks

Friday, November 29, 2013 11:14 PM - sanam

I saw your posts its so good I think that you solved my problem i were create a dropdownlist through viewmodel using selectlist and now my problem is i want to give links in each option of dropdown that i choose name from dropdown then the page is redirect according to link i hope that you understand my problem

Saturday, December 7, 2013 9:13 PM - Mike

@sanam

No I don't understand your problem.
Add your comment

If you have any comments to make about this article, please use this form to do so. Make sure that your comment relates specifically to the article above. More general comments can be posted through the form on the Contact page.

Please note, all comments are moderated, and some may not be published. The kind of things that will ensure your comment is deleted without ever seeing the light of day are as follows:

  • Not relevant to the article
  • Gratuitous links to your own site or product
  • Anything abusive or libellous
  • Spam
  • Anything in a language I don't understand including gibberish.

I do not pass email addresses on to spammers, so a valid one will assist me in responding to you personally if required.

Recent Comments

Allen Michaels 12/17/2014 4:37 PM
In response to Cascading DropDownLists with jQuery and ASP.NET
Fantastic thank you so much!...

Emily 12/17/2014 12:36 PM
In response to Parameterized IN clauses with ADO.NET and LINQ
Thanks, very helpful!!!! Can i use this for multiple in's ????? IN (.....) and IN(...) and IN...

sss 12/16/2014 3:06 PM
In response to Solving the Operation Must Use An Updateable Query error
good...

Gjuro 12/15/2014 10:30 PM
In response to Examining the Edit Methods and Edit View
You have one fromr (and it should be from, I suppose). :-)...

Gjuro 12/15/2014 10:27 PM
In response to Adding Search
Hi, thnx for all this C#->VB translations. Yet, the following code block is (slightly) in error it a...

Scot 12/14/2014 1:39 PM
In response to Entity Framework 6 Recipe - Alphabetical Paging In ASP.NET MVC
Thanks,Mike I found solution....

Gjuro 12/13/2014 10:52 PM
In response to Accessing Your Model's Data from a Controller
The article mentions "Creating an Entity Framework Data Model for an ASP.NET MVC Application" (at is...

Samuel 12/13/2014 8:40 AM
In response to Displaying The First n Characters Of Text
I have failed to use the extension because it throws an error that it doesn't recognise the chop be...

Ignas 12/12/2014 5:11 PM
In response to Cleaner Conditional HTML Attributes In Razor Web Pages
Any suggestions for Html Helper elements with HtmlAttributes, when you need to conditionally set it...

Gautam 12/11/2014 8:50 PM
In response to Validation In Razor Web Pages 2
Hi Mike Is this required for V3, non html helper input...