DropDownListFor Indexed Properties

On an MVC form I need to have a list of records with a dropdown for each record. I met several difficulties, such as binding to each record, and providing the (already) selected value as default value in each dropdown.

First, your Model class needs to contain the records in a List<> which is not null:

public class MyModel
{
  public List<MyRecord> Records { get; set; }
  public MyModel()
  {
    Records = new List<MyRecord>();
  }
}
public class MyRecord
{
  public int Id { get; set; }
  public int SelectedValue { get; set; }
}

Then the model can be rendered in the view like this (see here and here):

<%  for(var i=0; i<Model.Records.Count; i++)
    { %>
<%: Html.HiddenFor(m => m.Records[i].Id) %>
<%: Html.DropDownListFor(m => m.Records[i].SelectedValue, Model.MySelectList) %>
<%  } %>

The hidden input is needed to identify the indexed item within the list.

While the DropDownListFor() method works for direct Model properties, MVC3 seems to have a problem retrieving the value of an indexed data class. The current value is not selected, but always the first value in the dropdown.

I found two solutions: additionally providing the current value, or setting the Selected property.

The solutions I found on SO (here and here) explicitly provide the value, thus duplicating the value expression, and causing a typo to sneak in. But since we already have the expression (to identify the property), we might as well use the expression and calculate its value.

public static MvcHtmlString IndexedDropDownListFor<TModel, TProperty>(
  this HtmlHelper<TModel> htmlHelper, 
  Expression<Func<TModel, TProperty>> expression, 
  IEnumerable<SelectListItem> selectList)
{
  var val = expression.Compile()(htmlHelper.ViewData.Model);

  return htmlHelper.DropDownListFor(expression,
    new SelectList(
      selectList.Select(v => new SelectListItem { Text = v.Text, Value = v.Value }),
        "Value", "Text", val));
}

or

public static MvcHtmlString IndexedDropDownListFor<TModel, TProperty>(
  this HtmlHelper<TModel> htmlHelper, 
  Expression<Func<TModel, TProperty>> expression, 
  IEnumerable<SelectListItem> selectList)
{
  var val = expression.Compile()(htmlHelper.ViewData.Model);

  return htmlHelper.DropDownListFor(expression,
    new SelectList(
      selectList.Select(v => new SelectListItem 
       { Text = v.Text, Value = v.Value, 
         Selected = v.Value == val.ToString() }),
}

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: