为什么CheckBoxFor呈现额外的input标记,以及如何使用FormCollection获取值?

在我的ASP.NET MVC应用程序,我使用下面的代码呈现checkbox:

<%= Html.CheckBoxFor(i=>i.ReceiveRSVPNotifications) %> 

现在,我看到,这会呈现checkboxinput标记和隐藏的input标记。 我遇到的问题是当我尝试使用FormCollection从checkbox中检索值:

 FormValues["ReceiveRSVPNotifications"] 

我得到了“真实,虚假”的价值。 在查看呈现的HTML时,我可以看到以下内容:

  <input id="ReceiveRSVPNotifications" name="ReceiveRSVPNotifications" value="true" type="checkbox"> <input name="ReceiveRSVPNotifications" value="false" type="hidden"> 

所以FormValues集合似乎join了这两个值,因为它们具有相同的名称。

有任何想法吗?

看看这里:

http://forums.asp.net/t/1314753.aspx

这不是一个错误,实际上是Ruby on Rails和MonoRail使用的方法。

当您使用checkbox提交表单时,只有在checkbox被选中的情况下才会发布该值。 所以,如果你不选中这个checkbox,那么在很多情况下你都不想发送错误信息的时候,什么都不会发送到服务器。 由于隐藏的input名称与checkbox相同,因此如果取消选中checkbox,您仍然会收到发送给服务器的“false”。

当checkbox被选中时,ModelBinder将自动处理从“true,false”中提取“true”

我和肖恩(上图)有同样的问题。 这种方法对于POST来说可能很好,但是真的很糟糕。 因此,我实现了一个简单的Html扩展,只是把隐藏的领域。

 public static MvcHtmlString BasicCheckBoxFor<T>(this HtmlHelper<T> html, Expression<Func<T, bool>> expression, object htmlAttributes = null) { var result = html.CheckBoxFor(expression).ToString(); const string pattern = @"<input name=""[^""]+"" type=""hidden"" value=""false"" />"; var single = Regex.Replace(result, pattern, ""); return MvcHtmlString.Create(single); } 

我现在遇到的问题是,我不想改变MVC框架来破坏我的代码。 所以我必须确保我有testing范围解释这个新的合同。

我使用这种替代方法呈现GET表单的checkbox:

 /// <summary> /// Renders checkbox as ont input (normal Html.CheckBoxFor renders two inputs: checkbox and hidden) /// </summary> public static MvcHtmlString BasicCheckBoxFor<T>(this HtmlHelper<T> html, Expression<Func<T, bool>> expression, object htmlAttributes = null) { var tag = new TagBuilder("input"); tag.Attributes["type"] = "checkbox"; tag.Attributes["id"] = html.IdFor(expression).ToString(); tag.Attributes["name"] = html.NameFor(expression).ToString(); tag.Attributes["value"] = "true"; // set the "checked" attribute if true ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData); if (metadata.Model != null) { bool modelChecked; if (Boolean.TryParse(metadata.Model.ToString(), out modelChecked)) { if (modelChecked) { tag.Attributes["checked"] = "checked"; } } } // merge custom attributes tag.MergeAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); var tagString = tag.ToString(TagRenderMode.SelfClosing); return MvcHtmlString.Create(tagString); } 

这和Chris Kemp的方法很相似,这个方法工作的很好,除了这个方法没有使用底层的CheckBoxForRegex.Replace 。 它基于原始Html.CheckBoxFor方法的来源。

这是附加input标签的源代码 。 微软很友善地包含了正确解决这个问题的意见。

 if (inputType == InputType.CheckBox) { // Render an additional <input type="hidden".../> for checkboxes. This // addresses scenarios where unchecked checkboxes are not sent in the request. // Sending a hidden input makes it possible to know that the checkbox was present // on the page when the request was submitted. StringBuilder inputItemBuilder = new StringBuilder(); inputItemBuilder.Append(tagBuilder.ToString(TagRenderMode.SelfClosing)); TagBuilder hiddenInput = new TagBuilder("input"); hiddenInput.MergeAttribute("type", HtmlHelper.GetInputTypeString(InputType.Hidden)); hiddenInput.MergeAttribute("name", fullName); hiddenInput.MergeAttribute("value", "false"); inputItemBuilder.Append(hiddenInput.ToString(TagRenderMode.SelfClosing)); return MvcHtmlString.Create(inputItemBuilder.ToString()); } 

我认为最简单的解决scheme是直接渲染INPUT元素,如下所示:

 <input type="checkbox" id="<%=Html.IdFor(i => i.ReceiveRSVPNotifications)%>" name="<%=Html.NameFor(i => i.ReceiveRSVPNotifications)%>" value="true" checked="<%=Model.ReceiveRSVPNotifications ? "checked" : String.Empty %>" /> 

在Razor语法中,它更加容易,因为在给定“真正的”服务器端值时,“checked”属性直接以“checked”值呈现。