2

I have a bunch of checkboxes that are used for the user to specify which columns they want to see on a grid:

columnselector

At the moment, each checkbox has it's own key (which is essentially it's label name) and is declared like this in my view:

@Html.CheckBox(column.Key,
               (Request.Form[column.Key] == null ? true : 
                Request.Form[column.Key].Contains("true")),
               new { @class = "columnSelector" })

@Html.Label(column.HeaderText)

The issue is that I have to get the values from the form collection in my action, as otherwise I would have to have a bool parameter for every column selector checkbox. Alternatively, I thought I could name them all 'columnselection' or something and then it would be passed to my action as an array of values, however then I lose the context of the value since I don't have the column key.

I don't want to create a viewmodel with a property for each checkbox as this functionality is used on other screens with different columns and I would like to keep it generic.

Any thoughts on how I could achieve this column selection stuff in a clean and simple way?

dnatoli
  • 6,972
  • 9
  • 57
  • 96

1 Answers1

1

The Html.CheckBox method has a strange implementation. Checkboxes will have a unique name, and essentially a true/false value. This is so that the value will easily map to a bool parameter in an Action.

As you have obviously noticed, this makes for a tricky situation when your checkboxes are dynamic.

The solution is to generate your own checkboxes, all using a common name and having unique values. These will map very nicely into your Action!

See this question too, for some great examples and explanations.

Here's the desired Action:

public ActionResult SetSelectedColumns(string[] selectedColumns) {
    // selectedColumns will contain the keys of all the checked columns.
}

And here's how to get your HTML to map correctly:

<input type="checkbox" name="selectedColumns" value="@column.Key" @(column.Selected ? "checked='checked'" : "") />

I highly recommend putting this functionality into an extension method, so that you can also incorporate ModelState and htmlAttributes. Here's my untested attempt:

    public static HtmlString CheckBoxAlt(this HtmlHelper html, string name, string value, bool selected, object htmlAttributes)
    {
        var tag = new TagBuilder("input");
        if (htmlAttributes != null)
            tag.MergeAttributes(new RouteValueDictionary(htmlAttributes));
        tag.MergeAttribute("type", "checkbox");
        tag.MergeAttribute("name", name);
        tag.MergeAttribute("value", value);
        // Determine if this check should be selected:
        var state = html.ViewData.ModelState[name];
        if (state != null)
        {
            var values = (string[])state.Value.ConvertTo(typeof(string[]));
            selected = Array.IndexOf(values, value) != -1;
        }
        if (selected)
            tag.MergeAttribute("checked", "checked");

        return new HtmlString(tag.ToString(TagRenderMode.SelfClosing));
    }
Community
  • 1
  • 1
Scott Rippey
  • 15,614
  • 5
  • 70
  • 85