I find the added parameter in the query string looks a mess and makes people think that we as developers have done something wrong. So I've resorted to the extreme method of creating my own InputExtensions class which allows me to decide on whether I want the hidden input to be rendered or not.
The following is the InputExtensions class I've created (based on the existing MVC code to maintain full compatibility):
public static class InputExtensions
{
//
// Checkboxes
//
public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, bool>> expression, bool renderHiddenInput)
{
if (renderHiddenInput)
{
return System.Web.Mvc.Html.InputExtensions.CheckBoxFor(htmlHelper, expression);
}
return CheckBoxFor(htmlHelper, expression, false);
}
public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, bool>> expression, object htmlAttributes, bool renderHiddenInput)
{
if (renderHiddenInput)
{
return System.Web.Mvc.Html.InputExtensions.CheckBoxFor(htmlHelper, expression, htmlAttributes);
}
return CheckBoxFor(htmlHelper, expression, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes), false);
}
public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, bool>> expression, IDictionary<string, object> htmlAttributes,
bool renderHiddenInput)
{
if (renderHiddenInput)
{
return System.Web.Mvc.Html.InputExtensions.CheckBoxFor(htmlHelper, expression, htmlAttributes);
}
if (expression == null)
{
throw new ArgumentNullException("expression");
}
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
bool? isChecked = null;
if (metadata.Model != null)
{
bool modelChecked;
if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked))
{
isChecked = modelChecked;
}
}
return CheckBoxHelper(htmlHelper, metadata, ExpressionHelper.GetExpressionText(expression), isChecked, htmlAttributes);
}
private static MvcHtmlString CheckBoxHelper(HtmlHelper htmlHelper, ModelMetadata metadata, string name, bool? isChecked, IDictionary<string, object> htmlAttributes)
{
RouteValueDictionary attributes = ToRouteValueDictionary(htmlAttributes);
bool explicitValue = isChecked.HasValue;
if (explicitValue)
{
attributes.Remove("checked"); // Explicit value must override dictionary
}
return InputHelper(htmlHelper,
InputType.CheckBox,
metadata,
name,
value: "true",
useViewData: !explicitValue,
isChecked: isChecked ?? false,
setId: true,
isExplicitValue: false,
format: null,
htmlAttributes: attributes);
}
//
// Helper methods
//
private static MvcHtmlString InputHelper(HtmlHelper htmlHelper, InputType inputType, ModelMetadata metadata, string name, object value, bool useViewData, bool isChecked, bool setId, bool isExplicitValue, string format, IDictionary<string, object> htmlAttributes)
{
string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (string.IsNullOrEmpty(fullName))
{
throw new ArgumentException("Value cannot be null or empty.", "name");
}
var tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(htmlAttributes);
tagBuilder.MergeAttribute("type", HtmlHelper.GetInputTypeString(inputType));
tagBuilder.MergeAttribute("name", fullName, true);
string valueParameter = htmlHelper.FormatValue(value, format);
var usedModelState = false;
bool? modelStateWasChecked = GetModelStateValue(htmlHelper.ViewData, fullName, typeof(bool)) as bool?;
if (modelStateWasChecked.HasValue)
{
isChecked = modelStateWasChecked.Value;
usedModelState = true;
}
if (!usedModelState)
{
string modelStateValue = GetModelStateValue(htmlHelper.ViewData, fullName, typeof(string)) as string;
if (modelStateValue != null)
{
isChecked = string.Equals(modelStateValue, valueParameter, StringComparison.Ordinal);
usedModelState = true;
}
}
if (!usedModelState && useViewData)
{
isChecked = EvalBoolean(htmlHelper.ViewData, fullName);
}
if (isChecked)
{
tagBuilder.MergeAttribute("checked", "checked");
}
tagBuilder.MergeAttribute("value", valueParameter, isExplicitValue);
if (setId)
{
tagBuilder.GenerateId(fullName);
}
// If there are any errors for a named field, we add the css attribute.
ModelState modelState;
if (htmlHelper.ViewData.ModelState.TryGetValue(fullName, out modelState))
{
if (modelState.Errors.Count > 0)
{
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
}
}
tagBuilder.MergeAttributes(htmlHelper.GetUnobtrusiveValidationAttributes(name, metadata));
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.SelfClosing));
}
private static RouteValueDictionary ToRouteValueDictionary(IDictionary<string, object> dictionary)
{
return dictionary == null ? new RouteValueDictionary() : new RouteValueDictionary(dictionary);
}
private static object GetModelStateValue(ViewDataDictionary viewData, string key, Type destinationType)
{
ModelState modelState;
if (viewData.ModelState.TryGetValue(key, out modelState))
{
if (modelState.Value != null)
{
return modelState.Value.ConvertTo(destinationType, culture: null);
}
}
return null;
}
private static bool EvalBoolean(ViewDataDictionary viewData, string key)
{
return Convert.ToBoolean(viewData.Eval(key), CultureInfo.InvariantCulture);
}
}
Then you can call the method like so:
@Html.CheckBoxFor(t => t.boolValue, new { disabled="disabled" }, false)