I am using ASP.Net MVC 2 and have trouble getting a DropDownList to bind properly.
I am using a strongly typed ViewModel and the HTML.DropDownListFor statement to create the dropdown in my View. The statement works fine in the ‘normal’ scenario where I just bind the DropDown to a simple field in my ViewModel.
I am having trouble using this scenario when my ViewModel contain a List (array) of multiple objects where I want a field in each of these objects to bind to a (seperate) DropDown. In that case the DropDown’s do not get the correct initial value.
For example the case where we have a person that can have multiple addresses. Each address consists of a Street, a City and a State. I want the state to be displayed as a DropDown.
My model is:
public class PersonModel : CommonViewModel
{
public IList<SelectListItem> AvailableStates { get; set; }
public string Name { get; set; }
public IList<AddressModel> Addresses { get; set; }
public PersonModel()
{
AvailableStates = GenerateStates();
}
private IList<SelectListItem> GenerateStates()
{
List<SelectListItem> items = new List<SelectListItem>();
items.Add(new SelectListItem() { Text = "WA", Value = "WA" });
items.Add(new SelectListItem() { Text = "NY", Value = "NY" });
items.Add(new SelectListItem() { Text = "IH", Value = "IH" });
items.Add(new SelectListItem() { Text = "FL", Value = "FL" });
items.Add(new SelectListItem() { Text = "CA", Value = "CA" });
return items;
}
}
public class AddressModel
{
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
}
Now in my View I want to use something like:
<% using (Html.BeginForm("SubmitPersonDetails", "Home", new { actionMode = "Person"}, FormMethod.Post, new { id = "SubmitPersonForm" }))
{%>
<div class="formrow longinput">
<%= Html.LabelFor(person => person.Name)%>
<%= Html.TextBoxFor(person => person.Name)%>
</div>
<h4>Addresses</h4>
<% for (int i = 0; i < Model.Addresses.Count; i++) { %>
<div class="formrow">
<%= Html.TextBoxFor(person => person.Addresses[i].Street)%>
<%= Html.TextBoxFor(person => person.Addresses[i].City)%>
<%--<%= Html.TextBoxFor(person => person.Addresses[i].State)%>--%>
<%= Html.DropDownListFor(person => person.Addresses[i].State, Model.AvailableStates) %>
</div>
<% } %>
<input class="button primary" type="submit" value="Submit" name="Submit" />
<% } %>
For the Textboxes this works fine (also if I just use a textbox for State, see the commented out bit). The DropDown’s are generated correctly but not set to the correct value. If I select a value in the DropDowns and submit the form the correct values are put back into my model.
I also tried putting the Address part in a UserControl, which is then rendered several time. If I render the UserControl using Html.RenderPartial the correct values are set in the DropDOwn’s but on Postback my model isn’t populated correctly. If I render the UserControl using EditorFor I get the same problem as with the code above: the DropDown’s don’t get the correct initial value but on Postback I do get the selected values set in my model.
Another thing I tried is making the Addresses not a List but instead hardcoding several addresses in my View Model (properties: Address1, Address2, Address3) and then using:
<%= Html.DropDownListFor(person => person.Address1.State, Model.AvailableStates) %>
In this case the DropDown does get the correct value. Obviously I don’t want to use this workaround for lack of flexibility.
What am I doing wrong? I am new to MVC so maybe it is my whole approach that is wrong?
That’s a limitation of the
DropDownListForhelper which is not able to extract the value from a complex lambda expression such as the one you are using (a lambda expression containing array index access). One possible workaround is the following:It’s a bit ugly as we are now passing the same value in the SelectList constructor but this time the DropDownListFor helper will be able to properly set the corresponding option.