asp.net mvc 3 pre-select Html.DropDownListFor not

2019-02-06 06:03发布

问题:

Learning about dropdown lists, Im trying to add a RSVP create page for nerddinner as in Scott Gu's blog with a Html.DropDownListFor listing available dinners.

I can get the dropdown list populated but I cannot get the dropdown to pre select the value ("Sample Dinner 2") I want. Im using an initilizer to seed a few dinner objects in the db. The database is sql ce 4 using a EF 'code first approach'. Sorry I know this is a v common problem and hate to ask, but honestly have spent quite some time on this but cant get it to work:

ViewModel

public class RSVPViewModel
{
    public SelectList DinnersList { get; set; }
    public RSVP Rsvp { get; set; }
    public string SelectedItem { get; set; }
}

Controller

    //
    //GET: /RSVP/Create

    public ActionResult Create()
    {
        RSVP rsvp = new RSVP();
        string selected = "Sample Dinner 2";
        var typeList = new SelectList(dbc.Dinners.ToList(), "DinnerID", "Title", selected);

        var viewModel = new RSVPViewModel { DinnersList = typeList, Rsvp = rsvp, SelectedItem = selected };

        return View("Create", viewModel);
    }

View

@Html.DropDownListFor(model => model.Rsvp.DinnerID, Model.DinnersList)

HTML Result

<select data-val="true" data-val-number="The field DinnerID must be a number." data-val-required="The DinnerID field is required." id="Rsvp_DinnerID" name="Rsvp.DinnerID">
     <option value="1">Sample Dinner 1</option>
     <option value="2">Sample Dinner 2</option>
</select>

So is not preselecting the dropdownlist with the value "Sample Dinner 2" when the page loads. The list displays ok and sets the correct DinnerID when I make a selection and click Submit.

Tries this also:

@Html.DropDownListFor(x => x.SelectedItem, Model.DinnersList)

but doesnt set or bind to Rsvp.DinnerID.

This preselects from the list but doesnt bind (or set Rsvp.DinnerID)

@Html.DropDownList("DinnersList")

I want to keep it mvc3 so want to implement with strong type using ViewModel approach (no ViewData) and preferably using Html.DropDownListFor (not Html.DropDownList).Viewbag seems unnecessary for this case.

Thanks!

Edit1

Thinking I should be using a selectList of selectListItems I tried this verbose approach :

RSVP rsvp = new RSVP();
string selected = "2";
List<SelectListItem> dinners = new List<SelectListItem>();

foreach (Dinner dinner in dbc.Dinners.ToList())
{
    SelectListItem slDinner = new SelectListItem();
    slDinner.Value = dinner.DinnerID.ToString();
    slDinner.Text = dinner.Title;
    slDinner.Selected = (slDinner.Value == selected);
    dinners.Add(slDinner);
 }

 var dinnersList = new SelectList(dinners, "Value", "Text", selected);
 var viewModel = new RSVPViewModel { DinnersList = dinnersList, Rsvp = rsvp, SelectedItem = selected };

However still no work. Should I be making use of the ViewModel SelectedItem property in: @Html.DropDownListFor.. somehow? Something like :

@Html.DropDownListFor(x => x.SelectedItem, Model.DinnersList)

but how to I get a selected value to set Rsvp.DinnerID. I think thats called binding.

回答1:

After reading here and here, I finally understand how HtmlDropDownlistFor automatically selects the correct item in the dropdown based on your model - selecting a dinnerID in RSVP (foreign key to dinner.dinnerID) will cause the dropdown containing list of Dinner.DinnerIDs to pre select that value. No need yet I think for selectedValue in the SelectList or ViewModel.

Solution:

    //
    //GET: /RSVP/Create

    public ActionResult Create()
    {
        //automatically preselects matching DinnerID in the Dinner dropdownlist            
        var rsvp = new RSVP {DinnerID = 2 };

        var typeList = new SelectList(dbc.Dinners.ToList(), "DinnerID", "Title");
        var viewModel = new RSVPViewModel { DinnersList = typeList, Rsvp = rsvp};

        return View("Create", viewModel);
    }


回答2:

The SelectList constructor you're using is supposed to provide the selected value, but you are providing the selected text. You may want to try:

    int selected = 2;


回答3:

Html.DropDownList only works if the bound property is an int. Any other type, such as a named enum, will cause it to default to the first item.

Add a property to your Model, type int, which wraps the enum you are trying to maintain:

public myEnumType myProperty {get; set;} // don't bind this to Html.DropDownList

public myEnumType myPropertyAsInt {
    get {return (int)this.myProperty; }
    set {this.myProperty = (myEnumType)value; }
} // bind this instead

No need to use Selected in your SelectList - Html.DropDownList will synchronise just fine.



回答4:

Please take care if there is a QUERY STRING with same name , it will override that behavior, Not sure about HIDDEN FIELDS with same name.

E.g.

DropDownListFor will use the value of Query String of DinnerID if found



回答5:

Html.DropDownList accepts int properties, DropDownListFor too, but you have to be careful what you are doing. I examined the SelectExtensions.cs from ASP.NET MVC 3 and found this:

When you use DropDownList("XyField", "default") to create a DropDown, then you must place the select list into ViewBag.XyField and DropDownList() handles this correctly.

When you use DropDownListFor(m=>m.XyField, ... ), you can pass the select list explictly, like this: DropDownListFor(m=>m.XyField, ViewBag.XyFieldList as IEnumerable)

When you do this, this following happen:

  • The second parameter of DropDownListFor(...) will be used as source for the options
  • If there is a ModelState entry for "XyField", this will be used as the model value
  • If there is no model state AND Html.ViewData.Eval("XyField") returns not null, this value will be used as the model value.
  • If the found model value is not null, it will be converted to a string, using CultureInfo.CurrentCulture, and this value is compared with your SelectListItem values to preselect the list options.

Attention here: if you stored your select list in ViewBag.XyField, it will be found before the model is accessed. The don't want to compare "selectList.ToString()" with "selectList[i].Value" to preselect your options. Store your selection list in another place!

The funny thing is, you CAN use DropDownListFor with an implicit select list, in the same way as you expect it from DropDownList(). In this case, you will even store your list in ViewBag.XyField. To make this work, you simply have to call the helper with null as second parameter: DropDownListFor(m=>m.XyField, null)

Then, the select list is pulled from ViewBag.XyField and step 3 in the list above is skipped. So, if XyField is in the model state, this will take precedence before your own Selected properties in the select list, but otherwise, the select list will be used "as is".

Greetings Rolf