A Recipe Book for DropDownLists in ASP.NET MVC

September 5th, 2016

Today, we go from a beginner of SELECT tags to the mastery of DropDownLists, ListBoxes, and other types of lists in your MVC Views.

DropDownLists and ListBoxes are nothing new to UI design, but it seems to be a little harder to work with them in ASP.NET MVC.

In some forums around the net, I've noticed a number of people using ASP.NET MVC asking how to use a simple DropDownList for their Forms.

I know I mentioned a simple way to prepare DropDownLists in a ViewModel, but since then, I've improved some DropDownList techniques to make your dropdown coding a little easier.

Since my WebGrid Cookbook was such a hit and I haven't done another one yet, I thought today's post would be a perfect time for another one.

Let's get started.

What is a DropDownList/DropDownListFor?

The DropDownList() is the generic control for the <select>/<option> HTML tags where the DropDownListFor() works specifically with a Model.

For example, if I had a ViewModel like this:

var model = new HomeViewModel
{
    Selected = "2",
    OptionsSelectList = new List<SelectListItem>
    {
        new SelectListItem {Text = "Option 1", Value = "1"},
        new SelectListItem {Text = "Option 2", Value = "2"},
        new SelectListItem {Text = "Option 3", Value = "3"}
    }
};

Then the examples below would show how to use each one in a View. 

DropDownListFor() example

This:

@Html.DropDownListFor(e=> e.Selected, Model.OptionsSelectList);

renders this:

<select id="Selected" name="Selected">
    <option value="1">Option 1</option>
    <option selected="selected" value="2">Option 2</option>
    <option value="3">Option 3</option>
</select>

DropDownList() example

This:

@Html.DropDownList("MySelectList", Model.OptionsSelectList)

renders this:

<select id="MySelectList" name="MySelectList">
    <option value="1">Option 1</option>
    <option value="2">Option 2</option>
    <option value="3">Option 3</option>
</select>

What is a ListBox()/ListBoxFor()?

The ListBox() and ListBoxFor() are similar to the DropDownList/DropDownListFor, but instead of displaying one item with a dropdown, it displays all of the items in a box.

How can I make my ListBox bigger/longer?

By adding the size attribute to the ListBox/ListBoxFor HtmlAttributes at the end of the HtmlHelper.

@Html.ListBoxFor(e=> e.Selected, Model.OptionsSelectList, new { size="10" })

Which one is better to use: DropDownList() or DropDownListFor()?

Well, it depends (you knew that was coming, didn't you?).

Weigh these pros and cons and decide for yourself.

Use DropDownList() if you:

Use DropDownListFor() if you:

Personally, I would recommend using the DropDownListFor() (or any of the xxxFor() controls) primarily because of the default model binding that occurs from the FormCollection to the ViewModel automatically.

How do you set a default value for a DropDownList?

For the DropDownList, you can only do it in your List<SelectListItem> setup.

In our setup above, we need to add the Selected property to change whether it can be selected or not.

var model = new HomeViewModel
{
    Selected = "2",
    OptionsSelectList = new List<SelectListItem>
    {
        new SelectListItem {Text = "Option 1", Value = "1", Selected=false},
        new SelectListItem {Text = "Option 2", Value = "2", Selected=true},
        new SelectListItem {Text = "Option 3", Value = "3", Selected=false}
    }
};

With the DropDownListFor(), the Selected property is optional. If you notice the HtmlHelper from above, the DropDownListFor contains two parameters: Where we'll store the selected value as well as what's the default and the list of options they can pick from.

Personally, all of the generic controls (DropDownList(), ListBox(), TextBox(), etc.) don't have the flexibility and intuitiveness of the xxxxxFor() HtmlHelpers which makes the xxxFor() controls the better choice for your UI needs. 

How can I convert a List into a List<SelectListItem>?

After using a lot of DropDownLists over the year, you start to optimize your code.

I've used one DropDownList optimization that's floating around the Internet. It's the ToSelectList() extension method, but I've expanded on it.

The one thing I don't like about creating SelectListItems is you need to pass in the name of the property you want as your text and value. You know...the "magic strings."

Instead of fat-fingering a property name, I want to make sure I'm using the right property, so I went with a strong-typed ToSelectList().

public static class ListExtensions
{
    public static IEnumerable<SelectListItem> ToSelectList<T>(
        this IEnumerable<T> list, Func<T, string> dataField,
        Func<T, string> valueField, string defaultValue)
    {
        var result = new List<SelectListItem>();
        if (list.Any())
            result.AddRange(
                list.Select(
                    resultItem => new SelectListItem
                    {
                        Value = valueField(resultItem),
                        Text = dataField(resultItem),
                        Selected = defaultValue == valueField(resultItem)
                    }));
        return result;
    }
}

Now that we have this in our toolbelt, if we have a simple list of something...like Users:

public class User
{
    public string UserId { get; set; }
    public string Name { get; set; }
}

Our DropDownList is created using our new extension method:

var result = list.ToSelectList(e => e.Name, e => e.UserId, "2");

This makes building our lists of SelectListItems extremely easy to use.

How can I "autopost" a DropDownList when selecting a different item?

The trick here is to not use any C# or ASP.NET MVC, but HTML and JavaScript.

Make sure your DropDownList is located inside a Form you want posted.

Use a little JavaScript/jQuery to activate your auto postback.

$(function () {
    $('select#rows').change(function () {
        $("form").submit();
    });
});

Whatever you called your form, use that in the submit(). This will perform the postback to your controller (the [HttpPost] one), allow you to grab the value, act upon it, and redirect if necessary.

How do I create a grouped listbox?

It's all how you define the data. It's the exact same syntax in the View, but the SelectListItems must use another property called the Group property.

Let's say we have a list of Disney attractions.

var disneyRides = new List<Attraction>
{
    new Attraction {Id = 5, Title = "Space Mountain", Location = "Tomorrowland"},
    new Attraction {Id = 9, Title = "Stitches Escape", Location = "Tomorrowland"},
    new Attraction {Id = 2, Title = "Aladdin's Magic Carpets", Location = "Adventureland"},
    new Attraction {Id = 13, Title = "Big Thunder Mountain Railroad", Location = "Frontierland"},
    new Attraction {Id = 7, Title = "Pirates of the Carribean", Location = "Adventureland"},
    new Attraction {Id = 3, Title = "Monster's Inc. Laugh Floor", Location = "Tomorrowland"}
};

Our group property is Location. We need to use LINQ to group our items together.

var groupSelectList = new List<SelectListItem>();
var groupList = disneyRides.GroupBy(e => e.Location);
foreach (var grouping in groupList)
{
    var groupName = new SelectListGroup {Name = grouping.Key};
    groupSelectList.AddRange(grouping.Select(item => new SelectListItem
    {
        Text = item.Title,
        Value = item.Id.ToString(),
        Group = groupName
    }));
}

Once we group them, we can loop through the results and add them into a List of SelectListItems while setting our Group to groupName.

The syntax is the same in the View...

...however, the ViewModel is not what you expect though which brings me to my next recipe.

How do I pass default values to a ListBox?

A ListBox is a different animal.

When I started using a ListBox, I experienced something with the ViewModel.

If I want a simple selection, I would have a ViewModel that looks like this:

public class HomeViewModel
{
    public IEnumerable<SelectListItem> OptionsSelectList { get; set; }
    public string Selected { get; set; }
    public IEnumerable<SelectListItem> GroupedSelectList { get; set; }
    public string GroupSelected { get; set; }
}

For a simple ListBox, I wanted to pick one item and have it populate the Selected property.

Oh, no, no, no. ListBoxes allow for multiple items regardless of whether you want one or many.

We need to adjust our ViewModel to use the following types on the Selected/GroupSelected properties.

public class HomeViewModel
{
    public IEnumerable<SelectListItem> OptionsSelectList { get; set; }
    public string[] Selected { get; set; }
    public IEnumerable<SelectListItem> GroupedSelectList { get; set; }
    public string[] GroupSelected { get; set; }
}

They need to an array of values. Even if they select one, it will have a list of one.

Sneaky, must logical.

How do I create a "-- Select an Item --" for a DropDownList?

Quite simply, it's another item you add to your list of SelectListItems (in bold)

OptionsSelectList = new List<SelectListItem>
{
    new SelectListItem {Text = "--Select an Item--", Value = "0"},
    new SelectListItem {Text = "Option 1", Value = "1"},
    new SelectListItem {Text = "Option 2", Value = "2"},
    new SelectListItem {Text = "Option 3", Value = "3"}
},

If you already have your list generated, you can use the Insert method on the list to insert it at the beginning of the list:

model.OptionsSelectList.Insert(0, new SelectListItem
{
    Text = "--Select an Item--",
    Value = "0"
});

Of course, you'll need to change your OptionsSelectList from an IEnumerable<> to a List<> for this to work as expected.

Conclusion

As I mentioned with my WebGrid Cookbook, this will be a living document and I'll update it as people ask me questions or if I come across DropDownList or ListBox issues myself.

The DropDownLists are easy to build once you get the hang of them.

Usually, nowadays, it's the JavaScript wrapped around the DropDownLists that bother me.

Did this help you with your DropDownList issues? Did I miss a "recipe?" Post your comments below and we'll see if "the management" can sort it out.