Chosen Multiselect Dropdown List with ASP.Net MVC3

PIN

I recently came up against a UI where I needed to do some fancy stuff with the dropdown list. I decided to go with the Chosen Plugin. As I went on with my project, I quickly realized that multiselect dropdowns and pretty tricky with MVC3 (well, at least confirming to the MVC guidelines). While searching around, I noticed that a lot of other people also face a lot of issues with it. Therefore, I decided to write up a quick and simple guide that could get you going with fancying your dropdown lists.

Before you begin, make sure you have the latest version of the Chosen plugin referenced in a new MVC3 project. Once you have that, we will proceed to create a simple multiselect dropdown box (fancied up with Chosen) that lets the user select 2 favorite cars, from a list of 6. I tried making this project realistic so that any database storage and retrieval would also make sense.

As usual, we will begin with the model.  We would want our model to primarily keep track of cars, but specifically, for the dropdown to work, we want to keep track of selected cars, and a list of all cars. Here is how it would look:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace ChosenMultiSelectMVC3.Models
{
    public class MyModel
    {
        //The Values of selected items
        public string[] SelectedCars { get; set; }
        //The list of all cars
        public IEnumerable<SelectListItem> AllCars { get; set; }
    }
}

Of course, a more mature model would look a bit different. You would probably have a class for a car object. And your list wouldn’t be a generic SelectListItem — you get the point. For the purpose of this demo, this should do.

Once we have that, let us try to emulate a typical controller that would take values off a database, render it to a view and likewise, take values off a view and store it to the database. I have not set up a database for the demo, so I will replicate some of the database part from within the controller’s method. Here is how I have set up the controller.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using ChosenMultiSelectMVC3.Models;

namespace ChosenMultiSelectMVC3.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            ViewBag.Message = "The Chosen Plug-in Multi Select with MVC3 Demo";
            /*
             * Replicate what a DB would do here.
             * Get the current cars stored in the DB
             * that will be pre-selected as 
             * favorite cars... 
             */
            MyModel myModel = new MyModel();
            //Assume some selected values returned from DB
            myModel.SelectedCars = new string[] { "1", "2" };
            //Also the list of all cars
            myModel.AllCars = GetAllCars();

            return View(myModel);
        }

        /// <summary>
        /// Save cars to DB. Replicated what
        /// action would be performed to DB.
        /// </summary>
        /// <param name="myModel"></param>
        /// <returns>Saved message, or model errors.</returns>
        [HttpPost]
        public ActionResult SaveCars(MyModel myModel)
        {
            /*
             * Replicate what a DB would do here.
             * Get the selected cars from the view,
             * and store them in the DB, and return
             * the updated view.
             */
            if (ModelState.IsValid)
            {
                ViewBag.Message = "Changes have been saved.";
                return View(myModel);
            }
            return RedirectToAction("Index", myModel);
        }

        /// <summary>
        /// Usually this method will get the list of all cars
        /// from the database. But for the sake of this demo,
        /// we will replicate that by creating a list of cars.
        /// </summary>
        /// <returns>List of all cars</returns>
        private IEnumerable<SelectListItem> GetAllCars()
        {
            List<SelectListItem> allCars = new List<SelectListItem>();
            //Add a few cars to make a list of cars
            allCars.Add(new SelectListItem { Value = "1", Text = "BMW M3 Coupe" });
            allCars.Add(new SelectListItem { Value = "2", Text = "Aston Martin DB9" });
            allCars.Add(new SelectListItem { Value = "3", Text = "Lamborghini Aventador" });
            allCars.Add(new SelectListItem { Value = "4", Text = "Maserati Quattroporte" });
            allCars.Add(new SelectListItem { Value = "5", Text = "Audi R8" });
            allCars.Add(new SelectListItem { Value = "6", Text = "Mercedes SLS" });
            allCars.Add(new SelectListItem { Value = "7", Text = "Pagani Zonda R" });
            allCars.Add(new SelectListItem { Value = "8", Text = "Nissan GTR" });

            return allCars.AsEnumerable();
        }

    }
}

In the controller, I have a car list of total of 8 cars. I have also created the selected list (cars 1 and 2) to be pre-selected when the view is rendered, intended to emulate when you retrieve things from the database. Also, notice that the values of the SelectListItems are string not int. The MVC engine, ignores any int IDs, so be careful if you are creating your own car class, that either the ID needs to be a string, converted to string, or generated as unique Guid.

And finally, the view. In the view, we not only want to bind the dropdownlist with our model, but also activate the chosen plugin. Remember, this will be a strongly types view. Here is how it will look like.

@model ChosenMultiSelectMVC3.Models.MyModel

@{
    ViewBag.Title = "Index";
}

<link href="@Url.Content("~/Content/chosen.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/chosen.jquery.min.js")" type="text/javascript"></script>

<h2>@ViewBag.Message</h2>

<div>
    @Html.BeginForm("SaveCars","Home", FormMethod.Post) 
        @Html.LabelFor(m => m.SelectedCars, "Favorite Cars (2 Max.)") <br />
        @Html.ListBoxFor(m => m.SelectedCars, Model.AllCars,
            new { @class = "chosen", multiple = "multiple", style = "width: 350px;"})
        <input type="submit" value="Save" />
</div>

<script type="text/javascript">
    $(".chosen").chosen({ max_selected_options: 2 });
    $(".chosen-deselect").chosen({ allow_single_deselect: true });
    $(".chosen").chosen().change();
    $(".chosen").trigger('liszt:updated');
</script>

The JS script activates chosen, sets a max number for selects, along with some other options. Refer to the documentations for more details.

You can view the demo of this project here.

Well, that’s it. Good luck fancying up your own dropbox. Hope this solved some issue with the multi-select dropdownlist.