Model Binding to a Dropdown

Sample #1: Binding to a List<string>

There’s a few ways to model bind a collection to a dropdown using the DropDown HTML helpers in ASP.NET MVC. Let’s first look at a simple scenario, where we have a dropdown and we want to bind to a List of states.

For this example, we’re going to construct a class called Globalization that has the list of states. (I use an this object to hold look-up data sets.)

  public class Globalization
  {
    public List<string> States
    {
      get
      {
        return new List<string>() 
        {
          "NY", "NJ", "IL", "TX", "FL"
        };
      }
    }

Now let’s create our model:

public class Person
{
  [Required(ErrorMessage="Please choose a state!")]
  public string State { get; set; }
}

And follow up with our Controller:

  public class HomeController : Controller
  {
    Globalization global = new Globalization();
 
    public ActionResult Index()
    {
      return View();
    }
    Globalization global = new Globalization();
 
    public ActionResult Index()
    {
      return View();
    }
 
    [HttpPost]
    public ActionResult Index(Person ba, FormCollection form)
    {      
      // Let's check if the state form field exists in the Global State list...
      if (global.States.Exists(s => s == form["State"] ? true : false))
      {
        // Clear the errors from the state property of the modelstate
        ModelState["State"].Errors.Clear();
      }      
 
      // Let's put it in the ViewBag so we can retain the user's form state when 
      // the page is refreshed and the forms are repopulated with what the user
      // previously put in.       
      ViewBag.selectedState = form["State"];
 
      if (ModelState.IsValid)
      {
        // Run further server-side business logic from private methods.
        // DoFurtherStuff();
        return (RedirectToAction("Success"));
      }
 
      return View(ba);
    }
 
    public ActionResult Success()
    {
      return View("Success");
    }
  }

Now let’s do the View:

@model MvcApplication10.Models.Person
@using MvcApplication10.Models;
@{
  ViewBag.Title = "BankAccount";
  Html.EnableClientValidation(false);
  Html.EnableUnobtrusiveJavaScript(false);
}
 
@using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "mvcform" }))
{    
  <fieldset>
    <legend>BankAccount</legend>
 
    <div class="editor-field">      
      @{ 
        SelectList slStates = new SelectList(new Globalization().States, ViewBag.selectedState); 
      }
      @Html.DropDownList("State", slStates.OrderBy( x => x.Text ), "")
      @Html.ValidationMessageFor(model => model.State)
    </div>
 
    <p>
      <input type="submit" value="Create" />
    </p>
  </fieldset>
}
<div>
  @Html.ActionLink("Back to List", "Index")
</div>

So with this app, if we don’t pick a value from the dropdown, we get an error:

Now, let’s look at the HTML code generated:

<select id="State" name="State">
  <option value=""></option>
  <option>FL</option>
  <option>IL</option>
  <option>NJ</option>
  <option>NY</option>
  <option>TX</option>
</select>

Notice that the option elements don’t have explicit values, so they’ll be set to the text inside the option elements. So that’s equivalent to:

<select id="State" name="State">
  <option value=""></option>
  <option value="FL">FL</option>
  <option value="IL">IL</option>
  <option value="NJ">NJ</option>
  <option value="NY">NY</option>
  <option value="TX">TX</option>
</select>

We leave the first one blank so the user has to select a value. So what if we want to bind the dropdown to a key/value pair collection like Dictionary? Like this for example:

<select id="State" name="State">
  <option value="4523">FL</option>
  <option value="6345">IL</option>
</select>

To do the above, refer to the next section…

Sample #2: Binding to a List<SelectListItem>

Now let’s visualize a different scenerio:

Where the HTML generated is:

<select class="input-validation-error" id="CategoryModel_StatusID" name="CategoryModel.StatusID">
  <option selected="selected" value=""></option>
  <option value="1">Approval Code</option>
  <option value="2">More Info Requested</option>
  <option value="3">Rejected</option>
  <option value="4">Deleted</option>
  <option value="5">Approved</option>
</select>

and we have to populate it with the data from the db (Status table):

Where the schema is:

CREATE TABLE [dbo].[Status](
	[StatusID] [tinyint] IDENTITY(1,1) NOT NULL,
	[Name] [varchar](50) NOT NULL
 CONSTRAINT [PK_Status] PRIMARY KEY CLUSTERED 
(
	[StatusID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

So we write the C# code a little different for this scenario. I’ve found that for this, it’s helpful to use the helper @Html.DropDownListFor(), which accepts an IEnumerable, which you’ll have to construct.

First let’s create our Model class:

public class Category
{		
    [Required(ErrorMessage = "Status is required.")]
    public Byte StatusID { get; set; }
}

For convenience, let’s also create a ViewModel class that uses Category. We’re also using Dapper here, which makes db connection a snap. Check out the tutorial for more details.

  /// <summary>
  /// We create his class so we can map data from Dapper.
  /// </summary>
  public class StateSelectListItem
  {
    public string Name { get; set; }
    public byte StatusID { get; set; }
  }
 
  /// <summary>
  /// This is the ViewModel for the Category Create Form
  /// </summary>
  public class CategoryViewModel
  {
    public Category CategoryModel { get; set; }
 
    public List<SelectListItem> ListStatusCodes
    {
      get
      {
        // We're putting an empty SelectListItem so that the first item in the drop down
        // is blank. 
        List<SelectListItem> selectList = new List<SelectListItem>() { new SelectListItem() { Text = "", Value = "", Selected = false } };
 
        using (SqlConnection conn = new SqlConnection("Data Source=NARUTO;Initial Catalog=GalaxyM33;Integrated Security=True"))
        {
          conn.Open();
 
          // Let's map the results (using Dapper) to the list of StateSelectListItem
          IEnumerable listStatus = conn.Query<StateSelectListItem>("select StatusID, Name From [Status]");
 
          // We're adding SelectList objects to the List...
          foreach (StateSelectListItem item in listStatus)
          {
            selectList.Add(new SelectListItem() { Text = item.Name, Value = item.StatusID.ToString() });
          }
 
          conn.Close();
        }
        return selectList;
      }
    }
  }

Here’s the controller:

    public ActionResult Create()
    {
      CategoryViewModel viewModel = new CategoryViewModel();    
      return View(viewModel);
    }
 
    [HttpPost]
    public ActionResult Create(CategoryViewModel viewModel)
    {      
      return View(viewModel);
    }

Now for the View:

@model ViewModels.CategoryViewModel
@{
  ViewBag.Title = "Create";
  Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Create</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
  @Html.ValidationSummary(true)
  <fieldset>
    <legend>Category</legend>
    <div class="editor-field">      
      @Html.DropDownListFor(m => m.CategoryModel.StatusID, Model.ListStatusCodes)
      @Html.ValidationMessageFor(model => model.CategoryModel.StatusID)
    </div>       
    <p>
      <input type="submit" value="Create" />
    </p>
  </fieldset>
}
<div>
  @Html.ActionLink("Back to List", "Index")
</div>

That should do it.

Download Sample 1

Leave a Reply