MVC 4, Checkbox list and me

22,082

Solution 1

Right ok, I have got it sorted - hurrah! As you can see from the comments, there were a few issues that came up but please find below the complete solution which DOES work :D

Model

 public class CorporateDetails
    {

        public Guid? Id { get; set; }

        [Key]
        public int CorporateDetailId { get; set; }

        public int[] EmsId { get; set; }

        }

    public class EmsType
    {
        [Key]
        public int EmsId { get; set; }
        public string EmsName { get; set; }

        public virtual ICollection<EmsType> EmsTypes { get; set; }
    }

Controller

 public ActionResult Create()
    {
        CorporateDetails corporatedetails = new CorporateDetails();
        ViewBag.EmsId = new MultiSelectList(db.EmsTypes, "EmsId", "EmsName");
        return View(corporatedetails);
    }

Extension (placed in a folder in the root of the project)

 public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty[]>> expression, MultiSelectList multiSelectList, object htmlAttributes = null)
    {
        //Derive property name for checkbox name
        MemberExpression body = expression.Body as MemberExpression;
        string propertyName = body.Member.Name;

        //Get currently select values from the ViewData model
        TProperty[] list = expression.Compile().Invoke(htmlHelper.ViewData.Model);

        //Convert selected value list to a List<string> for easy manipulation
        List<string> selectedValues = new List<string>();

        if (list != null)
        {
            selectedValues = new List<TProperty>(list).ConvertAll<string>(delegate(TProperty i) { return i.ToString(); });
        }

        //Create div
        TagBuilder divTag = new TagBuilder("div");
        divTag.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);

        //Add checkboxes
        foreach (SelectListItem item in multiSelectList)
        {
            divTag.InnerHtml += String.Format("<div><input type=\"checkbox\" name=\"{0}\" id=\"{0}_{1}\" " +
                                                "value=\"{1}\" {2} /><label for=\"{0}_{1}\">{3}</label></div>",
                                                propertyName,
                                                item.Value,
                                                selectedValues.Contains(item.Value) ? "checked=\"checked\"" : "",
                                                item.Text);
        }

        return MvcHtmlString.Create(divTag.ToString());
    }

Extension registered in web config of the Views

 <pages pageBaseType="System.Web.Mvc.WebViewPage">
  <namespaces>
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Optimization"/>
    <add namespace="System.Web.Routing" />
    <add namespace="MyProject.Extensions" />
  </namespaces>
</pages>

View

@model Valpak.Websites.HealthChecker.Models.CorporateDetails
@{
    ViewBag.Title = "Create";
}
<h2>Create</h2>
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>CorporateDetails</legend>

           <div class="editor-label">
           @Html.CheckBoxListFor(model => model.EmsId, (MultiSelectList) ViewBag.EmsId)
          </div>          
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Which gives me a lovely list of check boxes. Hurrah!

Thanks Darin for your help, I've marked this as the answer but +50 for your time and effort.

Solution 2

Nice solution -

Just for others reference - I, like you, had run into a need for a checkboxlist - I have been using this here:

http://www.codeproject.com/Articles/292050/CheckBoxList-For-a-missing-MVC-extension

It works great... very well written - hope this can help someone.

Loren

Solution 3

No changes occurred in ASP.NET MVC 4 RC in this aspect and are unlikely to occur when it hits RTM.

But you could still implement a custom helper to achieve that. And you could even improve this helper so that it takes a lambda expression as first argument instead of a string in order to have strongly typed version.

And if you are not using enums here's another example.

Solution 4

If you pass selected value to MultiSelected (parameter #4)

ViewBag.VfonctionIds = new MultiSelectList(db.tbIntervenantFonctionTypes, "intervenantFonctionType_id", "Nom", fonctionSelected);  

Change the Helper to

        public static MvcHtmlString CheckBoxListFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty[]>> expression, MultiSelectList multiSelectList, object htmlAttributes = null)
    {
        //Derive property name for checkbox name
        MemberExpression body = expression.Body as MemberExpression;
        string propertyName = body.Member.Name;

        //Create div
        TagBuilder divTag = new TagBuilder("div");
        divTag.MergeAttributes(new RouteValueDictionary(htmlAttributes), true);

        //Add checkboxes
        foreach (SelectListItem item in multiSelectList)
        {
            divTag.InnerHtml += String.Format("<div><input type=\"checkbox\" name=\"{0}\" id=\"{0}_{1}\" " +
                                                "value=\"{1}\" {2} /><label for=\"{0}_{1}\">{3}</label></div>",
                                                propertyName,
                                                item.Value,
                                                (item.Selected) ? "checked=\"checked\"" : "",                                                    
                                                item.Text);
        }

        return MvcHtmlString.Create(divTag.ToString());
    }
Share:
22,082
Ricardo Deano
Author by

Ricardo Deano

Updated on July 05, 2022

Comments

  • Ricardo Deano
    Ricardo Deano almost 2 years

    Morning all.

    I can see this has been discussed elsewhere but was wondering if anything had change or things made simpler in MVC 4 for simpletons like me?!

    Scenario

    I have the following,edited, model:

    public class CorporateDetails
    {
    
        public Guid? Id { get; set; }
    
        [Key]
        public int CorporateDetailId { get; set; }
    
        public int? EmsId { get; set; }
        public string EmsName { get; set; }
    
        public virtual EmsType EmsType { get; set; }
    }
    
    public class EmsType
    {
        [Key]
        public int? EmsId { get; set; }
        public string EmsName { get; set; }
    
        public virtual ICollection<EmsType> EmsTypes { get; set; }
    }
    

    With the following standard create view:

     <fieldset>
        <legend>CorporateDetails</legend>
    
    
    
        <div class="editor-label">
            @Html.LabelFor(model => model.EmsId, "EmsType")
        </div>
        <div class="editor-field">
            @Html.DropDownList("EmsId", String.Empty)
            @Html.ValidationMessageFor(model => model.EmsId)
        </div>
        <div class="editor-label">
            @Html.LabelFor(model => model.EmsName)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.EmsName)
            @Html.ValidationMessageFor(model => model.EmsName)
        </div>
    
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
    

    This gives me, out of the box, a beautiful drop down list a la Scott Gu's blog

    Now my real question is this - how can I effectively convert this drop down box to what will effectively be a multi select,checkbox list?

    Again, apologies for going over trodden ground but I was just testing the water to see if any updates have occurred.

    Please note, first MVC project so go gently, I'm feeling very thick again :'(