54

I am building the following SelectList in my controller.

var u = new NewUser();

u.UserTypeOptions = new SelectList(new List<SelectListItem>
{
    new SelectListItem { Selected = true, Text = string.Empty, Value = "-1"},
    new SelectListItem { Selected = false, Text = "Homeowner", Value = ((int)UserType.Homeowner).ToString()},
    new SelectListItem { Selected = false, Text = "Contractor", Value = ((int)UserType.Contractor).ToString()},
});

return u;

And displaying it on my view like this:

@Html.DropDownListFor(m => m.UserType, Model.UserTypeOptions)

It looks like I am giving it a valid set of SelectListItems in what should be a pretty straightforward dropdown list, but instead of getting a valid <option> list with good values and text, I get this:

<select data-val="true" data-val-range="A user type must be selected." data-val-range-max="2" data-val-range-min="1" data-val-required="The UserType field is required." id="UserType" name="UserType" class="input-validation-error">
    <option>System.Web.Mvc.SelectListItem</option>
    <option>System.Web.Mvc.SelectListItem</option>
    <option>System.Web.Mvc.SelectListItem</option>
</select>

What gives? As far as I can tell, this should work.

Carsten
  • 11,287
  • 7
  • 39
  • 62
MattW
  • 12,902
  • 5
  • 38
  • 65
  • 1
    Why in the world would you create one `SelectList` and then create another `SeelctList` from it. And setting the `Selected` property of `SelectListItem` is just as pointless - its ignored by the HtmlHelper - its the value of property `UserType` that determines what is selected (the only time the `Selected` property is respected is when you create a dropdownlist that is not bound to your model (e.g. `@Html.DropDownList("NotAProperty", Model.UserTypeOptions)`) –  Dec 01 '15 at 22:23

4 Answers4

93

You are missing setting the Text and Value field in the SelectList itself. That is why it does a .ToString() on each object in the list. You could think that given it is a list of SelectListItem it should be smart enough to detect this... but it is not.

u.UserTypeOptions = new SelectList(
    new List<SelectListItem>
    {
        new SelectListItem { Selected = true, Text = string.Empty, Value = "-1"},
        new SelectListItem { Selected = false, Text = "Homeowner", Value = ((int)UserType.Homeowner).ToString()},
        new SelectListItem { Selected = false, Text = "Contractor", Value = ((int)UserType.Contractor).ToString()},
    }, "Value" , "Text", 1);

BTW, you can use a list or array of any type... and then just set the name of the properties that will act as Text and Value.

I think it is better to do it like this:

u.UserTypeOptions = new SelectList(
    new List<SelectListItem>
    {
        new SelectListItem { Text = "Homeowner", Value = ((int)UserType.Homeowner).ToString()},
        new SelectListItem { Text = "Contractor", Value = ((int)UserType.Contractor).ToString()},
    }, "Value" , "Text");

I removed the -1 item, and the setting of each item selected true/false.

Then, in your view:

@Html.DropDownListFor(m => m.UserType, Model.UserTypeOptions, "Select one")

This way, if you set the "Select one" item and don't set one item as selected in the SelectList, the UserType will be null (the UserType need to be int? ).

If you need to set one of the SelectList items as selected, you can use:

u.UserTypeOptions = new SelectList(options, "Value" , "Text", userIdToBeSelected);

As one of the users explained in the comments: The 4th option of the SelectList constructor is ignored when binding to a property using DropDownListFor() - it is the property's value that determines what is selected.

Romias
  • 13,783
  • 7
  • 56
  • 85
  • Using `new SelectList(...)` to create a 2nd identical `IEnumerable` from the first one is just pointless extra overhead. –  Nov 22 '17 at 07:11
  • 2
    And your last snippet is just plain wrong. The 4th option of the `SelectList` constructor is ignored when binding to a property using `DropDownListFor()` - its the value of the property which determines what is selected. –  Nov 22 '17 at 07:13
18

Just try this in razor

@{
    var selectList = new SelectList(
        new List<SelectListItem>
        {
            new SelectListItem {Text = "Google", Value = "Google"},
            new SelectListItem {Text = "Other", Value = "Other"},
        }, "Value", "Text");
}

and then

@Html.DropDownListFor(m => m.YourFieldName, selectList, "Default label", new { @class = "css-class" })

or

@Html.DropDownList("ddlDropDownList", selectList, "Default label", new { @class = "css-class" })
Mikael Dúi Bolinder
  • 2,080
  • 2
  • 19
  • 44
VnDevil
  • 1,321
  • 15
  • 13
6

Try this, just an example:

u.UserTypeOptions = new SelectList(new[]
    {
        new { ID="1", Name="name1" },
        new { ID="2", Name="name2" },
        new { ID="3", Name="name3" },
    }, "ID", "Name", 1);

Or

u.UserTypeOptions = new SelectList(new List<SelectListItem>
    {
        new SelectListItem { Selected = true, Text = string.Empty, Value = "-1"},
        new SelectListItem { Selected = false, Text = "Homeowner", Value = "2"},
        new SelectListItem { Selected = false, Text = "Contractor", Value = "3"},
    },"Value","Text");
Mikael Dúi Bolinder
  • 2,080
  • 2
  • 19
  • 44
Jaimin
  • 7,964
  • 2
  • 25
  • 32
  • seems to be the best option with the least overhead. For example, many of the answers above want you to use List with requires an include. – John Lord Aug 28 '20 at 20:24
0
var selectList =   new List<SelectListItem>();
selectList.Add(new SelectListItem {Text = "Google", Value = "Google"}) ;
selectList.Add(new SelectListItem {Text = "Other", Value = "Other"}) ;
Saeed Zhiany
  • 2,051
  • 9
  • 30
  • 41
  • 1
    Please read [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). While this code block may answer the OP's question, this answer would be much more useful if you explain how this code is different from the code in the question, what you've changed, why you've changed it and why that solves the problem without introducing others. – Saeed Zhiany Jul 08 '22 at 07:17