How can I get this ASP.NET MVC SelectList to work?
Asked Answered
P

23

128

I create a selectList in my controller, to display in the view.

I'm trying to create it on the fly, sorta thing .. like this...

myViewData.PageOptionsDropDown = 
   new SelectList(new [] {"10", "15", "25", "50", "100", "1000"}, "15");

It compiles, but the output is bad...

<select id="PageOptionsDropDown" name="PageOptionsDropDown">
    <option>10</option>
    <option>15</option>
    <option>25</option>
    <option>50</option>
    <option>100</option>
    <option>1000</option>
</select>

Notice how no item is selected?

How can I fix this??

Psychological answered 23/4, 2009 at 14:23 Comment(2)
Six answers... one favorited... no upvotes :/ I'll give it a +1Huai
possible duplicate of How can I add an item to a SelectList in ASP.net MVCSelimah
D
128

This is how I do it

IList<Customer> customers = repository.GetAll<Customer>();
IEnumerable<SelectListItem> selectList = 
    from c in customers
    select new SelectListItem
    {
        Selected = (c.CustomerID == invoice.CustomerID),
        Text = c.Name,
        Value = c.CustomerID.ToString()
    };

At second glance I'm not sure I know what you are after...

Deprive answered 23/4, 2009 at 14:33 Comment(2)
Doesn't fix the selected item issue but it is a great way of avoiding magic strings for select lists! CheersExemplum
To fix the selected item issue, add the following code: SelectList sList = new SelectList(selectList, "Text", "Value", selected); where selected is the current selected customerMicahmicawber
N
68

I use an extension method:

usage

var departmentItems = departments.ToSelectList(d => d.Code + 
                                               " - " + d.Description,
                                               d => d.Id.ToString(),
                                               " - ");

var functionItems = customerFunctions.ToSelectList(f => f.Description, 
                                                   f => f.Id.ToString(), 
                                                   " - ");

with

public static class MCVExtentions
{
    public static List<SelectListItem> ToSelectList<T>(
        this IEnumerable<T> enumerable, 
        Func<T, string> text, 
        Func<T, string> value, 
        string defaultOption)
    {
        var items = enumerable.Select(f => new SelectListItem()
                                     {
                                         Text = text(f), 
                                         Value = value(f) 
                                     }).ToList();
        items.Insert(0, new SelectListItem()
                    {
                        Text = defaultOption, 
                        Value = "-1" 
                    });
        return items;
    }
}
Nook answered 18/5, 2009 at 13:49 Comment(1)
nice. I've added a selected Func, a defaultValue option and a second overload without any defaults to specify and this works a treat. By the way if you want to make the return type IEnumerable<SelectListItem> you can use the yield return syntax to make this look really cleanBreaststroke
C
48

Using the constructor that accepts items, dataValueField, dataTextField, selectedValue as parameters :

ViewData["myList"] = 
                new SelectList(new[] { "10", "15", "25", "50", "100", "1000" }
                .Select(x => new {value = x, text = x}), 
                "value", "text", "15");

Then in your view :

<%=Html.DropDownList("myList") %>
Complicate answered 23/4, 2009 at 14:35 Comment(5)
nice dude :) i like! i was trying to do this .. but i couldn't get the .Select(..) part .. nice :) I'll try it out at home, later :)Psychological
Heya Cagdas. works great, except the selected field is not selected. any idea? is this a bug in the MVC stuff?Psychological
Selected item works when you add the SelectList item into ViewData object (as in ViewData["myList"] = new SelectList ..) and then render it with <%=Html.DropDownList("myList") %>. I couldn't get the selected item to work without doing it this way though. Weird.Pickup
@Cagdas - hi again dude .. i still haven't figured this out :( What's this about a ViewData??? I have a strongly typed ViewData with it's own property.... ???Psychological
@Psychological - I took that property from your example in the question. Now I'm editing the code sample. Please see my edit in a few seconds.Pickup
T
20

Building off Thomas Stock's answer, I created these overloaded ToSelectList methods:

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

public static partial class Helpers
{
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, bool selectAll = false)
    {
        return enumerable.ToSelectList(value, value, selectAll);
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, object selectedValue)
    {
        return enumerable.ToSelectList(value, value, new List<object>() { selectedValue });
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, IEnumerable<object> selectedValues)
    {
        return enumerable.ToSelectList(value, value, selectedValues);
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, bool selectAll = false)
    {
        foreach (var f in enumerable.Where(x => x != null))
        {
            yield return new SelectListItem()
            {
                Value = value(f).ToString(),
                Text = text(f).ToString(),
                Selected = selectAll
            };
        }
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, object selectedValue)
    {
        return enumerable.ToSelectList(value, text, new List<object>() { selectedValue });
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, IEnumerable<object> selectedValues)
    {
        var sel = selectedValues != null
            ? selectedValues.Where(x => x != null).ToList().ConvertAll<string>(x => x.ToString())
            : new List<string>();

        foreach (var f in enumerable.Where(x => x != null))
        {
            yield return new SelectListItem()
            {
                Value = value(f).ToString(),
                Text = text(f).ToString(),
                Selected = sel.Contains(value(f).ToString())
            };
        }
    }
}

In your controller, you might do the following:

var pageOptions = new[] { "10", "15", "25", "50", "100", "1000" };
ViewBag.PageOptions = pageOptions.ToSelectList(o => o, "15" /*selectedValue*/);

And finally in your View, put:

@Html.DropDownList("PageOptionsDropDown", ViewBag.PageOptions as IEnumerable<SelectListItem>, "(Select one)")

It will result in the desired output--of course, you can leave out the "(Select one)" optionLabel above if you don't want the first empty item:

<select id="PageOptionsDropDown" name="PageOptionsDropDown">
<option value="">(Select one)</option>
<option value="10">10</option>
<option selected="selected" value="15">15</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="1000">1000</option>
</select>

Update: A revised code listing can be found here with XML comments.

Telegraph answered 4/3, 2011 at 2:33 Comment(0)
E
13

The problem is, SelectList works as designed. The bug is in the design. You may set the Selected Property in SelectedItem, but this will completely be ignored, if you traverse the list with the GetEnumerator() (or if Mvc does that for you). Mvc will create new SelectListItems instead.

You have to use the SelectList ctor with the SelectListItem[], the Text-Name, the Value-Name and the SelectedValue. Be aware to pass as SelectedValue the VALUE of SelectListItem, which you want to be selected, not the SelectListItem itself! Example:

SelectList sl = new SelectList( new[]{
  new SelectListItem{ Text="one", Value="1"},
  new SelectListItem{ Text="two", Value="2"},
  new SelectListItem{ Text="three", Value="3"}
}, "Text", "Value", "2" );

(not tested this, but I had the same problem)

then the 2nd option will get the selected="selected" attribute. That looks like good old DataSets ;-)

Euryale answered 6/8, 2009 at 18:15 Comment(0)
P
11

This is an option:

myViewData.PageOptionsDropDown = new[] 
{
 new SelectListItem { Text = "10", Value = "10" },
 new SelectListItem { Text = "15", Value = "15", Selected = true }
 new SelectListItem { Text = "25", Value = "25" },
 new SelectListItem { Text = "50", Value = "50" },
 new SelectListItem { Text = "100", Value = "100" },
 new SelectListItem { Text = "1000", Value = "1000" },
}
Piny answered 24/6, 2009 at 16:50 Comment(2)
new SelectListItem { Selected=true, Text = "15", Value = "15" } for the selected value.Derisive
oops, yeah, forgot to include 'Selected' property, thanks Cadoo =)Piny
A
10

If that's literally all you want to do then just declaring the array as string fixes the selected item problem:

myViewData.PageOptionsDropDown = 
   new SelectList(new string[] {"10", "15", "25", "50", "100", "1000"}, "15");
Aligarh answered 15/1, 2011 at 18:2 Comment(0)
W
8

It's very simple to get SelectList and SelectedValue working together, even if your property isn't a simple object like a Int, String or a Double value.

Example:

Assuming our Region object is something like this:

public class Region {
     public Guid ID { get; set; }
     public Guid Name { get; set; }
}

And your view model is something like:

public class ContactViewModel {
     public DateTime Date { get; set; }
     public Region Region { get; set; }
     public List<Region> Regions { get; set; }
}

You can have the code below:

@Html.DropDownListFor(x => x.Region, new SelectList(Model.Regions, "ID", "Name")) 

Only if you override the ToString method of Region object to something like:

public class Region {
     public Guid ID { get; set; }
     public Guid Name { get; set; }

     public override string ToString()
     {
         return ID.ToString();
     }
}

This have 100% garantee to work.

But I really believe the best way to get SelectList 100% working in all circustances is by using the Equals method to test DropDownList or ListBox property value against each item on items collection.

Warty answered 28/7, 2012 at 23:8 Comment(0)
I
5

It seems if you have a strongly typed view you need to change the ID of the dropdown so that it is NOT the name of a property on the inherrited class. You then need to put some logic in your edit (POST) method to pull off the selected value from the FORMCollection and put it on to your instance before committing your changes.

This is certainly a little strange, but i tried it and it works.

So if you class has a field called CountryId say, and you're displaying a list of country names, make the dropdown have an id of CountryName rather than CountryId, then in the post, you can do something with Collection["CountryName"].

Illiquid answered 28/7, 2009 at 11:23 Comment(1)
Yes, I had this same problem. Simply changing the name worked. Thanks!Modillion
O
4

I had the exact same problem. The solution is simple. Just change the "name" parameter passed to the DropDownList helper to something that does not match any of the properties existing in your ViewModel. read more here: http://www.dotnetguy.co.uk/post/2009/06/25/net-mvc-selectlists-selected-value-does-not-get-set-in-the-view

I quote the Dan Watson:

In MVC if the view is strongly typed the selectlist’s selected option will be overridden and the selected option property set on the constructor will never reach the view and the first option in the dropdown will be selected instead (why is still a bit of a mystery).

cheers!

Occupational answered 5/9, 2011 at 18:1 Comment(0)
K
3

All these answers look great, but seems to be that the controller is preparing data for a View in a well-known structured format vs. letting the view simply iterate an IEnumerable<> delivered via the model and build a standard select list then let DefaultModelBinder deliver the selected item back to you via an action parameter. Yes, no, why not? Separation of concerns, yes? Seems odd to have the controller to build something so UI and View specific.

Kwabena answered 26/1, 2011 at 19:38 Comment(0)
O
3

Simple:

string[] someName = new string[] {"10", "15", "25", "50", "100", "1000"};
myViewData.PageOptionsDropDown = new SelectList(someName, "15");
Othelia answered 17/8, 2011 at 23:44 Comment(0)
L
2

I just ran it like this and had no problems,

public class myViewDataObj
    {
        public SelectList PageOptionsDropDown { get; set; }
    }

public ActionResult About()
        {
            myViewDataObj myViewData = new myViewDataObj();
            myViewData.PageOptionsDropDown =
                  new SelectList(new[] { "10", "15", "25", "50", "100", "1000" }, "15");

            ViewData["myList"] = myViewData.PageOptionsDropDown;
            return View();
        }

and

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

it also worked if you do this,

public ActionResult About()
        {
            myViewDataObj myViewData = new myViewDataObj();
            myViewData.PageOptionsDropDown =
                  new SelectList(new[] { "10", "15", "25", "50", "100", "1000" });

            ViewData["myListValues"] = myViewData.PageOptionsDropDown;
            ViewData["myList"] = "15";
            return View();
        }

and

<%=Html.DropDownList("myList",(IEnumerable<SelectListItem>)ViewData["myListValues"]) %>
Licensee answered 21/7, 2009 at 13:21 Comment(0)
D
1

Using your example this worked for me:

controller:

ViewData["PageOptionsDropDown"] = new SelectList(new[] { "10", "15", "25", "50", "100", "1000" }, "15");

view:

<%= Html.DropDownList("PageOptionsDropDown")%>
Derisive answered 26/6, 2009 at 17:26 Comment(1)
Yes that's working for me too. In my case, the list is an Entity Model object. Controller: ViewData["Categories"] = new SelectList(db.Categories.ToList(), "CategoryId", "CategoryName", "15"); View: Html.DropDownList("CategoryId", (SelectList)ViewData["Categories"], "--select--")Shevlo
W
1
MonthRepository monthRepository = new MonthRepository();
IQueryable<MonthEntity> entities = monthRepository.GetAllMonth();
List<MonthEntity> monthEntities = new List<MonthEntity>();

foreach(var r in entities)
{
    monthEntities.Add(r);
}

ViewData["Month"] = new SelectList(monthEntities, "MonthID", "Month", "Mars");
Wormhole answered 16/3, 2010 at 12:35 Comment(0)
P
1

I do it like this:

List<SelectListItem> list = new List<SelectListItem>{
new SelectListItem {Selected = true, Text = "Select", Value = "0"},
new SelectListItem {Selected = true, Text = "1", Value = "1"},
new SelectListItem {Selected = true, Text = "2", Value = "2"}
};
return list.ToArray();

The ToArray() takes care of the problems.

Phototopography answered 30/9, 2010 at 7:55 Comment(0)
G
1

If you want to pass some random text to your DropDownList, for example --Select-- you can easy do this using this code:

@Html.DropDownListFor(x => x.CategoryId, new SelectList(Model.Categories, "Id", "Name"), "--Select--", new { @class = "form-control" })
Gravante answered 4/1, 2016 at 17:50 Comment(0)
T
0

It may be the case that you have some ambiguity in your ViewData:

Take a look Here

Texture answered 23/4, 2009 at 14:42 Comment(0)
R
0

the value selected in the model takes advantage instead of the default item. (I agree I didn't read all posts)

Rhodic answered 28/3, 2010 at 16:33 Comment(0)
M
0

I can't remember how mvc 1 was setup, but it seems that it wanted the select list named the same as the field it belonged too...

What I found, as someone kind of said above, is that my select lists weren't working in mvc2 when the ViewData they were sent as was named the same as the field.

For example:

<%= Html.DropDownListFor((model => model.ForID), (SelectList)ViewData["ForName"]) %>

works when

<%= Html.DropDownListFor((model => model.ForID), (SelectList)ViewData["ForID"]) %>

does not work as the ViewData name "ForID" is named the same as the field it is working for

Mckay answered 14/4, 2010 at 4:43 Comment(0)
S
0

A possible explanation is that the selectlist value that you are binding to is not a string.

So in that example, is the parameter 'PageOptionsDropDown' a string in your model? Because if it isn't then the selected value in the list wouldn't be shown.

Schear answered 13/12, 2010 at 3:58 Comment(0)
G
0

If you look in the source code for MVC 2 at the Html.DropDownList extension method, it never checks the SelectList class SelectedValue property. It will only ever try to match against your Model.

All the above are all variations on a theme, ie how do you send a bunch of data to the view to for a drop-down list & they're all as good as each other (more-or-less).

The problem is in the view. Either create your own DropDownList extension method that does respect the selectvalue you set, or iterate though by hand. Which ever works best for you.

Ger answered 3/1, 2011 at 15:39 Comment(1)
@Franz Antesberger says much the same thing.Ger
M
0

If you have a collection in your model and your View is strongly type, some variation of this will work:

@Html.DropDownListFor(x => x.RegionID, 
    new SelectList(Model.Regions,"RegionID", "RegionName", Model.RegionID))

-or-

@Html.DropDownList("RegionID", 
    new SelectList(Model.Regions, "RegionID", "RegionName", Model.RegionID))
Mckay answered 30/9, 2011 at 17:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.