ASP.NET MVC DropDownLists - Multiple Selection and Enum Support

4.48 (33 votes)

My original article about Drop Down Lists in ASP.NET MVC has proven to be among the most popular on this site, amassing over a third of a million views since it was published 5 years ago. This article builds on the original by looking at using drop down lists for multiple selections, and the helper that was introduced in MVC 5.1 to support enumerations as a source of select options.

Multiple Selections

Just to recap, an MVC Html.DropDownList helper is rendered to the browser as an HTML <select> element. They are mainly employed to provide the user with a means to select one of a number of options. Multiple selection select elements are useful when you want to provide your user with a means to choose more then one option but you do not want to use checkboxes, for example. You can enable multiple selection in one of two ways: you can add the multiple attribute to the Html.DropDownList helper via its htmlAttributes parameter, or you can use the Html.ListBox (or Html.ListBoxFor) helper. Options can be passed to the helper in a number of forms: as an Enumerable<SelectListItem>; as a SelectList object or as a MultiSelectList object:

using (var db = new NorthwindContext())
{
    var categories = db.Categories.Select(c => new { 
        CategoryID = c.CategoryID, 
        CategoryName = c.CategoryName 
    }).ToList();
    ViewBag.Categories = new MultiSelectList(categories, "CategoryID", "CategoryName");
    return View();
}

 

@Html.DropDownList("CategoryId", (MultiSelectList)ViewBag.Categories, new { multiple = "multiple"})
@Html.ListBox("CategoryId", (MultiSelectList)ViewBag.Categories)

Both helpers will result in identical HTML:

<select id="CategoryId" multiple="multiple" 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>

MVC DropDownLists 2 

There aren't a lot of styling options for multiple select lists, but you can affect the number of list item initially visible by default (4) by setting a value for the size attribute:

@Html.ListBox("CategoryId", (MultiSelectList)ViewBag.Categories, new { size = 8 })

MVC DropDownLists 2 

When a multiple select control is posted to the server, the collection of selected values are processed by ASP.NET into a comma-separated list. The MVC model binding system quite happily sees this collection as an array. If the selected values can be parsed into ints, you can use an int array to represent the posted values. The following image shows a select list generated from the code above being posted back to the server. The selected items have values of 1, 3 and 7, which are captured in the categoryId parameter. The parameter name matches the name given to the DropDownList.

MVC DropDownLists 2

 

Setting Selected Values

You often need to set the selected values of a multiple select control, such as in editing scenarios or if you want to provide the user with a default selection. You can achieve this in a number of ways depending on preference and the helper that you use to generate the multiple select control. The SelectList class doesn't support setting multiple items as selected, so it is not an option, but you can use either the MultiSelectList or a collection of SelectListItem objects. The following code shows the selected values being set as 1, 3 and 7 when using the MultiSelectList approach:

using (var db = new NorthwindContext())
{
    var categories = db.Categories.Select(c => new { 
        CategoryID = c.CategoryID, 
        CategoryName = c.CategoryName 
    }).ToList();
    ViewBag.Categories = new MultiSelectList(categories, "CategoryID", "CategoryName", new[]{1,3,7});
    return View();
}

Here's the alternative approach that passes a List<SelectListItem> to the view:

using (var db = new NorthwindContext())
{
    var selected = new[] { 1, 3, 7 };
    var categories = db.Categories
        .AsEnumerable()
        .Select(c => new SelectListItem { 
            Value = c.CategoryID.ToString(), 
            Text = c.CategoryName,
            Selected = selected.Contains(c.CategoryID)
        }).ToList();
    ViewBag.Categories = categories;
    return View();
}

The MultiSelectList approach seems to win in terms of user-friendliness.

Html.ListBoxFor and view models

The Html.ListBoxFor helper is the strongly typed version designed for use with view models. Here's a simple view model to illustrate how to use the ListBoxFor helper:

public class CategoryViewModel
{
    public int[] CategoryId { get; set; }
    public MultiSelectList Categories { get; set; }
}

The view model class has two properties - an array of ints to represent the selected values and a MultiSelectList to hold the options. Here is a snippet showing the view code:

@Html.ListBoxFor(model => model.CategoryId, Model.Categories, new { size = 8 })

And here is the code to populate the options:

using (var db = new NorthwindContext())
{
    var model = new CategoryViewModel();
    var categories = db.Categories.Select(c => new
    {
        CategoryID = c.CategoryID,
        CategoryName = c.CategoryName
    }).ToList();
    model.Categories = new MultiSelectList(categories, "CategoryID", "CategoryName");
    model.CategoryId = new[] { 1, 3, 7 };
    return View(model);
}

In this example, I have explicitly set the view model's CategoryId property with the array that holds the selected values. The ListBoxFor helper will mark each of these options as selected in the rendered HTML. You can just as easily pass the integer array in to the MultiSelectList method as shown earlier:

using (var db = new NorthwindContext())
{
    var model = new CategoryViewModel();
    var categories = db.Categories.Select(c => new
    {
        CategoryID = c.CategoryID,
        CategoryName = c.CategoryName
    }).ToList();
    model.Categories = new MultiSelectList(categories, "CategoryID", "CategoryName", new[] { 1, 3, 7 });
    return View(model);
}

The resulting HTML will be the same.

Enumerations as a source for options

ASP.NET MVC 5.1 saw the introduction of a new helper: Html.EnumDropDownListFor. As a strongly typed helper, this works with enum properties in view models. It takes the members of the enum and produces a select list with them, assigning the value to the option's value attribute and the enumerator to the text. Here's an example of an enum:

public enum Power
{
    Coal = 1,
    Gas,
    Hydro,
    Nuclear,
    Solar,
    Wave,
    Wind
}

This might be used to represent a selection of power generation options. Here's how it might appear as part of a view model:

public class PowerViewModel
{
    public Power Power { get; set; }
}

And this is how the helper is used to render the options in the view:

@model PowerViewModel

<form method="post">
    @Html.EnumDropDownListFor(model => model.Power)
    <div>
        <input type="submit" />
    </div>
</form>

By default, the option with a value of 0 is selected. In this case, the enum values were intialised at 1 so the helper added an option with a value of 0 and empty text and made it selected:

MVC5 DropDownLists

If you want another option to be selected, you can set that in the view model:

var model = new PowerViewModel();
model.Power = Power.Hydro;
return View(model);

If you prefer a default option that says "Pick One" or similar, you can pass that value into the second argument for the helper method:

@Html.EnumDropDownListFor(model => model.Power, "Pick One")

If your enum starts at 0, this will result in an extra option with an empty value appearing in the list. However, the first enum (in my example, Coal) will still be selected by default. If you start your enum from 1, the "Pick One" option will be generated with a value of 0, which will be selected by default and will also form a valid selection if you make the property required via the DataAnnotation [Required] attribute. So what if you don't want the "Pick One" option being assigned a value and/or don't want the option with a value of 0 to be selected by default? You can solve both these problems by making the enum nullable in your view model:

public class PowerViewModel
{
    public Power? Power { get; set; }
}

And of course, you can still force the user to pick a valid value by adding the [Required] attribute to the property in the view model.

Finally, to retrieve the selected value, you ensure that the enum is included in the parameter list belonging to the action method that the form posts to, either on its own or as part of a view model. Here's the selected value being caught by the debugger when the form is posted back to the controller:

MVC5 DropDownLists 2

 

Summary

This article completes my look at dropdownlists in ASP.NET MVC. It looks at how to manage multiple selections and explores the new helper introduced in MVC 5.1 to support the use of enumerations in dropdownlists. If you would like to review the more common uses of the DropDownList helpers, please take some time to read the first article.

 

You might also like...

Date Posted:
Last Updated:
Posted by:
Total Views to date: 184465

16 Comments

- Satyabrata Mohapatra

Great article.

- Bigmachini

This just made my day, afternoon, evening, night... was looking for a way of doing this without having to use javascript. Thank you.. quick question how would you persist the results in a database though?

- Mike

@Bigmachini,

I cover that in a previous article on managing many to many relationships with Entity Framework: http://www.mikesdotnetting.com/article/109/asp-net-mvc-entity-framework-one-to-many-and-many-to-many-inserts

- Senad Mustafa

Hi Mike,

Thanks for the articles on dropdownlists. They are really great but I think you are missing one useful example; how do you populate dropdownlists depending on choice made on another droplist? Or how about multiselection on one list giving the appropriate selections in the next ddl?
Or, God forbid, this devious issue, selection of a choice in ddl A fires a selection of choices in ddl B which in its turn loads selection in ddl c.
(Park names-> Categories -> Items)

These are everyday issues but by all the gods that be, what an issue to solve via programminig. Especially via MVC. I am not sure if this is possible to do without using friggin JS or JQuery, may they be cursed forever.

Mike, I really hope you will have time to make good examples of this as I have searched through the net for the latest thirteen hours without managing to solve my issues with this. I hope to find a well done example of how to solve these issues.

Best regards, SM

- Mike

@SM,

I may add an article in time that shows how to cascade your dropdownlists in MVC, but I must warn you in advance that it will feature the use of jQuery. There's no way to do this without JavaScript and jQuery does help to simplify that.

- Scott

Excellent! Just what I need for the current application I am working on. Even to the extent that I need a multi select list of Categories! You know. You have already helped me immensely at the ASP.Net web site.

- RVI

Hi,
I am using the enumDropDownListFor helper provided in mvc5. How can I provide multiple selection in the enumDropDownList? Could you provide example?

- Mike

@RVI

You can use an overload of the EnumDropDownLostFor helper that takes an object representing htmlAttributes and pass multiple = "multiple" to it:
@Html.EnumDropDownLostFor(m =>  m.Power, new {multiple = "multiple"})
A list of available overloads

- Rodrigo Porcionato

Mike, you are the best!

- Nabeel

Good One...!

- Heemanshu

why you are using static this new { }

static one is of no use i think

- Mike

@Heemanshu

I don't understand you. Sorry.

- Stephen Welgemoed

This really helped me out. Thank you!

- Anvesh

what if we are taking postback values from FormCollections instead of an array int[] category. How it works?

thanks

- Mike

@Anvesh,

You can use string.Split on the FormCollection item to convert it to an array of strings. Then you can use LINQ to project that into an array of ints.
var values = Request["formfield"].Split(new []{','}).Select(s => Convert.ToInt32(s)).ToArray();

It's a lot simpler to add an array of ints to the Action method parameter list and let the modelbinder do all that for you, in my opinion.

- Benji

You the Men. The second time I find your page helping me tru my exsam

Recent Comments

Pam 30/08/2017 11:30
In response to Sending Email in Razor Pages
Mike, RazorPages sound like a nice choice for somebody still working in ASP classic who wants to to...

Robby Robson 15/08/2017 00:43
In response to Routing in Razor Pages
Mike: great stuff. Now that .Core Standard 2.0 is formally out, how soon will you rewrite your book...

Satyabrata Mohapatra 28/07/2017 08:59
In response to Sending Email in Razor Pages
Bit off topic, but congratulation sir for your MVP award. You deserve it !!!...

Satyabrata Mohapatra 23/07/2017 16:43
In response to Razor Pages - The Elevator Pitch
@Dale Severin You can continue to build apps using asp.net web pages....

Satyabrata Mohapatra 23/07/2017 16:40
In response to Sending Email in Razor Pages
Thanks for sharing...learned a lot...

Gfw 22/07/2017 11:53
In response to Sending Email in Razor Pages
Question... Does System.Net.Mail support SSL?...

Dale Severin 20/07/2017 03:38
In response to Razor Pages - The Elevator Pitch
I work with razor web pages extensively. I appreciate the rapid development it permits me to I am as...

Obinna Okafor 14/07/2017 01:19
In response to Routing in Razor Pages
Thank you, Mike. Good post....

Satyabrata Mohapatra 11/07/2017 16:02
In response to Routing in Razor Pages
Very powerful routing system!!...

Cyrus 05/07/2017 03:41
In response to Razor Pages - Getting Started With The Preview
How can I trim packages and services as much as possible to use just razor pages? I don’t want to to...