Crazy idea... Asp.Net MVC should just accept checked checkboxes as "true" when passed to bools in models... .
I think the below - where a ModelBinder accepts the HTML standard "on" to mean true - should've always been the default implementation in Asp.Net MVC. This solution is for the Classic/Non-Core, but, it should be easy to adapt to Core.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace Brass9.Web.Mvc.ModelBinders
{
public class FixedCheckboxFormModelBinder : System.Web.Mvc.IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (
// Form POST
!controllerContext.HttpContext.Request.ContentType.StartsWith
("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase)
/*
// Note: This is implied - by the way we add this ModelBinder to the global app list (typeof(bool))
||
bindingContext.ModelMetadata.ModelType != typeof(bool)
*/
)
{
return null;
}
string name = bindingContext.ModelName;
var valueProviderResult = bindingContext.ValueProvider.GetValue(name);
if (valueProviderResult.AttemptedValue == "on")
{
var replacementResult = new ValueProviderResult(true, "on", System.Globalization.CultureInfo.CurrentCulture);
bindingContext.ModelState.SetModelValue(name, replacementResult);
return true;
}
return null;
}
}
}
Then enable it in Global.asax.cs, in Application_Start()
:
ModelBinders.Binders.Add(typeof(bool), new Brass9.Web.Mvc.ModelBinders.FixedCheckboxFormModelBinder());
So, we just build a custom ModelBinder, filter just for Model values expecting a bool coming in over form POST, and passing us the HTML standard "on" - safely limiting its intervention to checkboxes.
It's actually sort of strange trying to apply this fix, because most documentation about ModelBinders is praise with very little in the way of clear how-tos.
Why we solved it this way:
We're migrating an older app to entirely use original Asp.Net MVC (non-Core). Not only would moving all the checkboxes over to @Html.Checkbox...
(much wasn't written this way) take a very long time, it also produces a lot of undesirable results, because of the extra, unnecessary hidden input, and the difficulty of migrating pages over. For example, we know there are some pages that have Javascript walking the DOM expecting elements in a specific order, that the hidden input would break, and don't want to comb through every single page looking for these bugs.