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

Viewed 231160

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??

23 Answers

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...

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;
    }
}

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") %>

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 ;-)

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" },
}

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");

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"].

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.

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"]) %>

Using your example this worked for me:

controller:

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

view:

<%= Html.DropDownList("PageOptionsDropDown")%>
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");

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

Take a look Here

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.

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.

Related