I am trying to create an HTML helper to display a set of checkboxes for an enum and have those values bound to a List<enum> on my view model.

Most of this is fine, we can use Enum.GetValues() to generate checkbox for each value in the enum. e.g. for this enum and view model:

Enum Fruit {
    Apple,
    Banana
}

Class MyViewModel {
    public List<Fruit> Fruits { get; set; }
}

We can generate this:

<input id="Fruits_Apple" name="Fruits" type="checkbox" value="Apple">
<input id="Fruits_Banana" name="Fruits" type="checkbox" value="Banana">

and the model binder will correctly bind things when the user submits a form.

The issue I'm running into is how to cope with the case where no checkboxes are selected, and we need to re-display the page (e.g. because validation of another field has failed).

In this case the built in Html Helpers will re-display using data from ModelState (rather than the view model). This also works fine with my code as long as at least one checkbox in the set is checked. However, when no checkboxes are checked then nothing is sent to the browser for that field, and so there is no ModelState value for the Fruits field, so the helper falls back to the view model data (which may not match what the user just selected).

The built-in CheckboxFor() method works around this by including a hidden field set to false in the page. That means that the ModelState always contains at least a false value, and sometimes both a false and a true. When both values are present the model binder sets the value on the view model to true.

I have tried adding a hidden field to my checkbox list:

<input id="Fruits_Apple" name="Fruits" type="checkbox" value="Apple">
<input id="Fruits_Banana" name="Fruits" type="checkbox" value="Banana">
<input name="Fruits" type="hidden" value="hidden_checkbox_dummy_value">

This ensures that there is always ModelState, however the ModelBinder can't map hidden_checkbox_dummy_value to a value of Fruit so we get an exception.

Having written this all out, I now suspect that my way forward is to investigate a custom model binder to ignore the dummy value, but this took enough investigation that I think this question may still be useful to others in the future.

Confirmation that a custom model binder will/will not work, or other suggestions on a way forward would be appreciated.

Please note that adding an extra value to the enum is not a solution I can use as it makes re-use too messy.

0

There are 0 best solutions below