Question

I have an asp.net-mvc website and i am running into an issue where the correct selected item in a dropdown is not being selected. I have the following code on my controller action (simplified to isolate the issue):

public ActionResult MyAction()
{
 var vm = GetVM();

 var list = new List<INamed> {new NamedInfo() {Id = 1, Name = "Yes"}, new NamedInfo() {Id = 0, Name = "No"}};
 vm.YesNoList = SelectListHelper.GenerateDropdownList(vm.IncludesWeekends ? 1 : 0, list);

  return View(vm);
 }

and here is the definition of GenerateDropdownList

    public static List<SelectListItem> GenerateDropdownList<T>(int id, IEnumerable<T> list) where T : INamed
    {
        List<SelectListItem> dropdown = list.Select(c => new SelectListItem
        {
            Selected = c.Id == id,
            Text = c.ToString(),
            Value = c.Id.ToString()
        }).ToList();

        return dropdown;
    }

Below is the code in my HTML view:

 <% = Html.DropDownList("IncludesWeekends", Model.YesNoList, new { @id = "IncludesWeekends" })%>

I expect No to be selected in my example (and it has Selected = true when i put a breakpoint on the server side but when i look at the html that is generated, nothing is selected:

<select id="IncludesWeekends" class="autoComplete1" name="IncludesWeekends">
 <option value="1">Yes</option>
 <option value="0">No</option>
 </select>

and "Yes" is selected by default because its teh first item.

Any suggestions on what i am doing wrong here or alternatives that work?

Was it helpful?

Solution

The Selected property in SelectListItem is mostly useless (more about that in my answer here). The HTML helpers will ignore it in most cases. Instead, in your case, they will look at the values of:

  • ModelState["IncludesWeekends"]
  • ViewData["IncludesWeekends"]
  • Model.IncludesWeekends

... and turn whatever value they find into a String, then use that as the selected value.

You have an IncludesWeekends property on your model, so it will do:

Model.IncludesWeekends.ToString()

... which - judging from your code - will result in True" or "False" (since it's a bool).

In your case, you're using c.Id.ToString() as the <option> values for your dropdown, and since the helper won't find its chosen string ("True"/"False") among those values ("1"/"0"), it won't select anything.

Ways around it:

  1. (Easiest way): Accept using "True" and "False" as your <option> values rather than integer IDs.

  2. (The almost-as-easy-but-not-very-clean way): Set ViewData["IncludesWeekends"] to the value you want selected (i.e., the Id). This works, because the helper will look at that before Model.IncludesWeekends.

  3. Use a view model, where you have an IncludesWeekendsId property, then use that to generate the dropdown, rather than IncludesWeekends:

    <%= Html.DropDownList("IncludesWeekendsId", Model.YesNoList) %>

    or

    <%= Html.DropDownListFor(m => m.IncludesWeekendsId, Model.YesNoList) %>

    Then, when returning from the view, translate IncludesWeekendsId back into the proper value on your model.

In this case, I'd probably go with 1 if I wasn't in a pedantic mood. But solution 3 is a general, and mostly clean, way to solve this in cases where you have to use different values for your dropdown items than the value of your model's property converted to a string.

OTHER TIPS

Try use the Html.DropDownListFor-helper instead:

@Html.DropDownListFor(c => c.Id, Model.YesNoList)

Forget about the Selected = c.Id == id in:

    List<SelectListItem> dropdown = list.Select(c => new SelectListItem
    {
        Selected = c.Id == id,
        Text = c.ToString(),
        Value = c.Id.ToString()
    }).ToList();

Post your viewmodel if possible.

And I would recomend using the Razor syntax if you're able to change, your views looks so much cleaner! <% Html.DropDown....) %> becomes @Html.DropDown...)

Your code would be some much cleaner if you used an enum for that I would suggest you to use this approach instead it generates even cleaner and consistent code: https://stackoverflow.com/a/18381045/1654155

I'd advice you to use such a type-safe construction:

<% = Html.DropDownListFor(x => x.IncludesWeekends, 
                          new SelectList(Model.YesNoList, "Value", "Text"), 
                          new { @id = "IncludesWeekends" })%>

instead of this one:

<% = Html.DropDownList("IncludesWeekends", 
                        Model.YesNoList, 
                        new { @id = "IncludesWeekends" })%>

You gain a lot - strongly typed lambda expression tied to given model property instead of property name hardcoded as a magic string. Using this construction you automatically have intelli-sense, refactoring tools and compiler support. Strongly typed helpers exists in MVC since 2.0 version, so there is no point of using older ones.

The next thing, modify IncludesWeekends model value to preselect desired option. In your case it means changing the type of IncludesWeekends to string instead of bool and assign to it value "0" or "1". Alternatively, if you don't want to modify the type of IncludesWeekends add additional property to your model, e.g. SelectedValue and do some minor changes:

in the action:

vm.SelectedValue = vm.IncludesWeekends ? "1" : "0";

on the view:

Html.DropDownListFor(x => x.SelectedValue, ...

You don't need to use the Selected property of SelectListItem - ignore it and remove all references to it.

 public ActionResult Index()
 {
                ViewModel vm = new ViewModel();
                vm.IncludesWeekends = false;
                var list = new List<INamedInfo> { new NamedInfo() { Id = 1, Name = "Yes" }, new NamedInfo() { Id = 0, Name = "No" } };
                vm.YesNoList = GenerateDropdownList(vm.IncludesWeekends ? 1 : 0, list);
                vm.IncludesWeekendsValue = vm.IncludesWeekends ? 1 : 0;
                return View(vm);
 }


 public class ViewModel
{
        public List<SelectListItem> YesNoList { get; set; }
        public bool IncludesWeekends { get; set; }
        public int IncludesWeekendsValue { get; set; }

}

 <% = Html.DropDownListFor(m => m.IncludesWeekendsValue, Model.YesNoList, new { @id = "IncludesWeekends" })%>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top