【问题标题】:How to handle checkboxes in ASP.NET MVC forms?如何处理 ASP.NET MVC 表单中的复选框?
【发布时间】:2008-10-20 21:30:10
【问题描述】:

注意:这个问题已经超过九年了!

您最好的选择是搜索较新的问题,或者搜索下面的答案以查找您的特定 MVC 版本,因为这里的许多答案现在已经过时了。

如果您确实找到了适合您的版本的答案,请确保该答案包含您正在使用的 MVC 版本。
(原始问题从下面开始)


这对我来说似乎有点奇怪,但据我所知,这就是你的做法。

我有一组对象,我希望用户选择其中一个或多个。这对我说“带有复选框的表单”。我的对象没有任何“选择”的概念(它们是通过反序列化 wcf 调用形成的基本 POCO)。所以,我做了以下事情:

public class SampleObject{
  public Guid Id {get;set;}
  public string Name {get;set;}
}

在视图中:

<%
    using (Html.BeginForm())
    {
%>
  <%foreach (var o in ViewData.Model) {%>
    <%=Html.CheckBox(o.Id)%>&nbsp;<%= o.Name %>
  <%}%>
  <input type="submit" value="Submit" />
<%}%>

而且,在控制器中,这是我可以看到找出用户检查了哪些对象的唯一方法:

public ActionResult ThisLooksWeird(FormCollection result)
{
  var winnars = from x in result.AllKeys
          where result[x] != "false"
          select x;
  // yadda
}

首先它很奇怪,其次,对于用户检查的那些项目,FormCollection 将其值列为“真假”而不仅仅是真。

显然,我错过了一些东西。我认为这是基于这样的想法构建的,即在 html 表单中作用于集合中的对象是使用UpdateModel() 或通过 ModelBinder 更新的。

但是我的对象不是为此设置的;这是否意味着这是唯一的方法?还有其他方法吗?

【问题讨论】:

标签: asp.net-mvc forms checkbox


【解决方案1】:

Html.CheckBox 正在做一些奇怪的事情 - 如果您在结果页面上查看源代码,您会看到在每个复选框旁边生成了一个 &lt;input type="hidden" /&gt;,它解释了您为每个表单看到的“真假”值元素。

试试这个,它绝对适用于 ASP.NET MVC Beta,因为我刚刚尝试过。

把它放在视图中,而不是使用 Html.CheckBox():

<% using (Html.BeginForm("ShowData", "Home")) {  %>
  <% foreach (var o in ViewData.Model) { %>
    <input type="checkbox" name="selectedObjects" value="<%=o.Id%>">
    <%= o.Name %>
  <%}%>
  <input type="submit" value="Submit" />
<%}%>

你的复选框都叫selectedObjects,每个复选框的value是对应对象的GUID。

然后发布到以下控制器操作(或类似的操作,而不是 Response.Write())

public ActionResult ShowData(Guid[] selectedObjects) {
    foreach (Guid guid in selectedObjects) {
        Response.Write(guid.ToString());
    }
    Response.End();
    return (new EmptyResult());
}

这个例子将只写你选中的框的 GUID; ASP.NET MVC 为您将选中复选框的 GUID 值映射到 Guid[] selectedObjects 参数,甚至将 Request.Form 集合中的字符串解析为实例化的 GUID 对象,我认为这很不错。

【讨论】:

  • 是的!这也是我必须为我的应用程序做的事情!
  • wtf.与控件同名的隐藏输入字段。它的 ViewState 2.0 !
  • 这也出现在 1.0 版本中。查看@andrea-balducci 提交的答案,它为您提供了一种智能的方法来处理这个问题。如果未选中该复选框,则检索到的结果文本应为“false false”——这是解决脑死浏览器问题的好方法...
  • 惊喜?什么?隐藏的输入与复选框具有相同的名称 - 如果未选中同名的复选框,则不会发布其值,而会发布隐藏的值。浏览器第一次遇到命名元素时,它将使用该值并忽略所有其他具有相同名称的元素。这保证了一个值被提交:如果复选框被选中(假设它位于隐藏元素上方),则为 true;如果未选中复选框,则为 false(空复选框被忽略,隐藏成为后备)。真正的 wtf 是为什么没有其他人指出这一点。
  • @nerraga:你错了:拥有多个同名表单元素是有效的 html;如果是这样,则 all 元素已发布,我不确定是否实际定义了顺序(尽管实际上可能只是按页面顺序)。使用“false”这个词作为值有点误导,所以是的,这是一个 WTF - 一个更好、更少误导性的选择应该是像“exists”或像破折号这样的无意义的标记。
【解决方案2】:

HtmlHelper 添加一个隐藏输入来通知控制器未检查状态。 所以要获得正确的检查状态:

bool bChecked = form[key].Contains("true");

【讨论】:

  • 这是解决此问题的最简单方法,也是我一直使用的方法。它允许您使用辅助方法并考虑 ASP.NET MVC 行为。
  • .. 或者在未选中的情况下:bool bChecked = form[key].Equals("false");执行 .Contains("false") 失败,因为 true 值为 "true,false"。
【解决方案3】:

如果您想知道为什么他们会在其中放置一个与复选框同名的隐藏字段,原因如下:

来自源代码的注释 MVCBetaSource\MVC\src\MvcFutures\Mvc\ButtonsAndLinkExtensions.cs

为复选框渲染一个额外的<input type="hidden".../>。 这解决了以下情况 未选中的复选框不发送 请求。发送隐藏输入 可以知道 复选框出现在页面上时 请求已提交。

我猜想在幕后他们需要知道这一点才能绑定到控制器操作方法上的参数。然后你可以有一个我想的三态布尔值(绑定到一个可为空的 bool 参数)。我没有尝试过,但我希望他们就是这样做的。

【讨论】:

  • 是的,这在您有分页网格等的情况下很方便,并且您想取消选择之前在某些业务对象中选择的项目。
【解决方案4】:

您还应该使用&lt;label for="checkbox1"&gt;Checkbox 1&lt;/label&gt;,因为这样人们就可以点击标签文本以及复选框本身。它的样式也更容易,至少在 IE 中,当您通过页面的控件切换时,它会突出显示。

<%= Html.CheckBox("cbNewColors", true) %><label for="cbNewColors">New colors</label>

这不仅仅是“哦,我能做到”的事情。它显着增强了用户体验。即使不是所有用户都知道他们可以点击很多人会点击的标签。

【讨论】:

    【解决方案5】:

    我很惊讶这些答案都没有为此使用内置的 MVC 功能。

    我写了一篇关于这个here 的博客文章,它甚至实际上将标签链接到了复选框。我使用 EditorTemplate 文件夹以干净和模块化的方式完成此任务。

    您最终会在 EditorTemplate 文件夹中得到一个新文件,如下所示:

    @model SampleObject
    
    @Html.CheckBoxFor(m => m.IsChecked)
    @Html.HiddenFor(m => m.Id)
    @Html.LabelFor(m => m.IsChecked, Model.Id)
    

    在您的实际视图中,无需循环,只需 1 行代码:

    @Html.EditorFor(x => ViewData.Model)
    

    访问我的blog post了解更多详情。

    【讨论】:

    • 谢谢! Everboyd 仍然使用旧的表单方式。当您提交时,您可以在没有所有这些 collection[] 和 request.form 垃圾的情况下访问模型 - 这就是我想要的。 +1 和+啤酒
    • 这应该是该问题的最佳答案。非常简单且易于实施。
    • 在找到这个答案之前,我苦苦挣扎了一段时间才能找到一个优雅的解决方案。它已经有几年的历史了,但在 2016 年仍然完全有效!如果在多个实例中使用,请使用内置的 SelectListItem 作为非常适合此的泛型类型
    • SampleObject 是一个列表吗?如:@ModelType List(Of Type)
    【解决方案6】:

    这就是我一直在做的事情。

    查看:

    <input type="checkbox" name="applyChanges" />

    控制器:

    var checkBox = Request.Form["applyChanges"]; if (checkBox == "on") { ... }

    我发现 Html.* 辅助方法在某些情况下不是很有用,而且我最好用普通的旧 HTML 来做。这是其中之一,另一个想到的是单选按钮。

    编辑:这是在预览版 5 上,显然是版本之间的 YMMV。

    【讨论】:

    • 我只想添加一个替代示例:Object.Property = !String.IsNullOrEmpty(Request.Form["NAME"]);
    • 如果未选中则进入异常
    【解决方案7】:

    他们似乎选择只读取第一个值,所以当复选框被选中时这是“真”,当只包含隐藏值时是“假”。这很容易用这样的代码获取:

    model.Property = collection["ElementId"].ToLower().StartsWith("true");
    

    【讨论】:

      【解决方案8】:

      @Dylan Beattie 很棒的发现!!!我非常感谢你。为了进一步扩展,这种技术也可以完美地与视图模型方法配合使用。 MVC 太酷了,它很聪明,可以将 Guid 数组绑定到与绑定到 View 的 Model 对象同名的属性。示例:

      视图模型:

      public class SampleViewModel
      {
          public IList<SampleObject> SampleObjectList { get; set; }
          public Guid[] SelectedObjectIds { get; set; }
      
          public class SampleObject
          {
              public Guid Id { get; set; }
              public string Name { get; set; }
          }
      }
      

      查看:

      <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
      <h2>Sample View</h2>
      <table>
          <thead> 
              <tr>
                  <th>Checked</th>
                  <th>Object Name</th>
              </tr>
          </thead> 
      <% using (Html.BeginForm()) %>
      <%{%>                    
          <tbody>
              <% foreach (var item in Model.SampleObjectList)
                 { %>
                  <tr>
                      <td><input type="checkbox" name="SelectedObjectIds" value="<%= item.Id%>" /></td>
                      <td><%= Html.Encode(item.Name)%></td>
                  </tr>
              <% } %>
          </tbody>
      </table>
      <input type="submit" value="Submit" />
      <%}%>                    
      

      控制器:

          [AcceptVerbs(HttpVerbs.Get)]
          public ActionResult SampleView(Guid id)
          {
              //Object to pass any input objects to the View Model Builder 
              BuilderIO viewModelBuilderInput = new BuilderIO();
      
              //The View Model Builder is a conglomerate of repositories and methods used to Construct a View Model out of Business Objects
              SampleViewModel viewModel = sampleViewModelBuilder.Build(viewModelBuilderInput);
      
              return View("SampleView", viewModel);
          }
      
          [AcceptVerbs(HttpVerbs.Post)]
          public ActionResult SampleView(SampleViewModel viewModel)
          {
              // The array of Guids successfully bound to the SelectedObjectIds property of the View Model!
              return View();
          }
      

      任何熟悉 View Model 哲学的人都会很高兴,这就像一个冠军!

      【讨论】:

      • 谢谢,您的 SampleViewModel 解决了原始答案中的一些困惑。
      【解决方案9】:

      我还想指出,您可以为每个复选框命名一个不同的名称,并将该名称作为 actionresults 参数的一部分。

      例子,

      查看:

       <%= Html.CheckBox("Rs232CheckBox", false, new { @id = "rs232" })%>RS-232
      
       <%= Html.CheckBox("Rs422CheckBox", false, new { @id = "rs422" })%>RS-422
      

      控制器:

      public ActionResults MyAction(bool Rs232CheckBox, bool Rs422CheckBox) {
          ...
      }
      

      视图中的值被传递给动作,因为名称相同。

      我知道这个解决方案不适合您的项目,但我想我会把这个想法扔掉。

      【讨论】:

        【解决方案10】:
        <input type = "checkbox" name = "checkbox1" /> <label> Check to say hi.</label>
        

        来自控制器:

            [AcceptVerbs(HttpVerbs.Post)]
            public ActionResult Index(FormCollection fc)
            {
        
                 var s = fc["checkbox1"];
        
                 if (s == "on")
                 {
                     string x = "Hi";
                 }
            }
        

        【讨论】:

          【解决方案11】:

          这个问题也在 1.0 版中出现。 Html.Checkbox() 会导致添加另一个隐藏字段,其名称/ID 与原始复选框相同。当我尝试使用 document.GetElemtentsByName() 加载复选框数组时,您可以猜到事情是如何搞砸的。这很奇怪。

          【讨论】:

            【解决方案12】:

            据我所知,模型不想猜测checked = true还是false,我通过在提交表单之前使用jQuery在checkbox元素上设置一个value属性来解决这个问题:

             $('input[type="checkbox"]').each(function () {
                   $(this).attr('value', $(this).is(':checked'));
             }); 
            

            这样,您不需要隐藏元素来存储复选框的值。

            【讨论】:

              【解决方案13】:

              我知道这个问题是在 MVC3 还没有出现的时候写的,但是对于遇到这个问题并使用 MVC3 的任何人,你可能想要“正确”的方法来做到这一点。

              虽然我认为做整个

              Contains("true");
              

              东西很棒而且很干净,适用于所有 MVC 版本,问题是它没有考虑文化(好像在 bool 的情况下它真的很重要)。

              至少在 MVC3 中,计算布尔值的“正确”方法是使用 ValueProvider。

              var value = (bool)ValueProvider.GetValue("key").ConvertTo(typeof(bool));
              

              当我编辑权限时,我会在我客户的一个网站中执行此操作:

              var allPermissionsBase = Request.Params.AllKeys.Where(x => x.Contains("permission_")).ToList();
              var allPermissions = new List<KeyValuePair<int, bool>>();
              
              foreach (var key in allPermissionsBase)
              {
                   // Try to parse the key as int
                   int keyAsInt;
                   int.TryParse(key.Replace("permission_", ""), out keyAsInt);
              
                   // Try to get the value as bool
                   var value = (bool)ValueProvider.GetValue(key).ConvertTo(typeof(bool));
              }
              

              现在,它的美妙之处在于您可以将它用于几乎任何简单的类型,甚至根据文化(比如金钱、小数等),它也是正确的。

              ValueProvider 是当你像这样形成你的动作时使用的:

              public ActionResult UpdatePermissions(bool permission_1, bool permission_2)
              

              但是当您尝试动态构建这些列表并检查值时,您永远不会在编译时知道 Id,因此您必须即时处理它们。

              【讨论】:

              • 您是否有理由使用 Request.Params 而不是在方法中添加 FormCollection 参数?
              • 没有理由,我真的不认为一种方式比另一种方式有任何好处。
              • 对不起,其实是有原因的。刚刚查看了我的代码,我正在使用这个方法进行 5 次不同的调用,我不想从每个方法中传递 FormCollection。这是无需传递任何东西即可获得密钥的最简单方法。
              • 是的,我是 MVC 新手。而且我在某处读到,通常使用 FormCollection 而不是 Request.Params 更好,这与使用类型化模型而不是 FormCollection 相同(因此实际上最好使用模型并避免使用 Request.Params)。我注意到没有使用代码的其他内容,例如 allPermissions 变量和 keyAsInt。感谢您的回答。
              【解决方案14】:

              最简单的方法就是这样......

              您设置名称和值。

              &lt;input type="checkbox" name="selectedProducts" value="@item.ProductId" /&gt;@item.Name

              然后在提交时抓取复选框的值并保存在一个 int 数组中。 然后是适当的 LinQ 函数。就是这样。。

              [HttpPost]
                      public ActionResult Checkbox(int[] selectedObjects)
                      {
                          var selected = from x in selectedObjects
                                         from y in db
                                         where y.ObjectId == x
                                         select y;                   
              
                          return View(selected);
                      }
              

              【讨论】:

                【解决方案15】:

                与 nautic20 的回答相同,只需使用与 ViewModel 中字符串/整数/枚举的集合属性同名的 MVC 默认模型绑定复选框列表即可。就是这样。

                但是需要指出一个问题。在每个复选框组件中,您不应将“Id”放入其中,这会影响 MVC 模型绑定。

                以下代码适用于模型绑定:

                 <% foreach (var item in Model.SampleObjectList)
                       { %>
                        <tr>
                            <td><input type="checkbox" name="SelectedObjectIds" value="<%= item.Id%>" /></td>
                            <td><%= Html.Encode(item.Name)%></td>
                        </tr>
                 <% } %>
                

                以下代码不会绑定到模型(这里的不同之处在于它为每个复选框分配了 id)

                <% foreach (var item in Model.SampleObjectList)
                       { %>
                        <tr>
                            <td><input type="checkbox" name="SelectedObjectIds" id="[some unique key]" value="<%= item.Id%>" /></td>
                            <td><%= Html.Encode(item.Name)%></td>
                        </tr>
                <% } %>
                

                【讨论】:

                • 感谢您的回答,但这个问题juuuust 有点老了。六岁左右。您可能想要edit 并指定您正在使用的 MVC 版本。这将帮助任何使用较新版本的人找到此答案。
                • 我同意这一点。在头疼和咬牙切齿之后,删除唯一 ID 解决了这个问题
                • 针对这个问题,我使用的 MVC 版本是 .net MVC 4.0。
                【解决方案16】:

                这是我在使用 Html.CheckBox(...

                Replace("true,false","true").Split(',')
                

                选中了 4 个框,未选中,未选中,选中了 真,假,假,假,真,假 进入 真,假,假,真。 正是我需要的

                【讨论】:

                  【解决方案17】:

                  这样的事情怎么样?

                  bool isChecked = false;
                  if (Boolean.TryParse(Request.Form.GetValues(”chkHuman”)[0], out isChecked) == false)
                      ModelState.AddModelError(”chkHuman”, “Nice try.”);
                  

                  【讨论】:

                    【解决方案18】:

                    当使用复选框 HtmlHelper 时,我更喜欢将发布的复选框表单数据作为数组使用。我真的不知道为什么,我知道其他方法有效,但我想我只是更喜欢尽可能将逗号分隔的字符串视为一个数组。

                    因此,进行“检查”或真实测试将是:

                    //looking for [true],[false]
                    bool isChecked = form.GetValues(key).Contains("true"); 
                    

                    做一个错误的检查会是:

                    //looking for [false],[false] or [false]
                    bool isNotChecked = !form.GetValues(key).Contains("true"); 
                    

                    主要区别在于使用GetValues,因为它以数组形式返回。

                    【讨论】:

                      【解决方案19】:

                      只需在$(document).ready 上执行此操作:

                      $('input:hidden').each(function(el) {
                          var that = $(this)[0];
                          if(that.id.length < 1 ) {
                      
                              console.log(that.id);
                              that.parentElement.removeChild(that);
                      
                          }
                      });
                      

                      【讨论】:

                      • 不幸的是,对于关闭 JavaScript 的人(NoScript 插件、旧手机、Lynx...),您可能会在服务器端得到奇怪的结果
                      【解决方案20】:

                      我的解决办法是:

                      <input type="checkbox"  id="IsNew-checkbox"  checked="checked" />     
                      <input type="hidden"  id="IsNew" name="IsNew" value="true"  />      
                      <script language="javascript" type="text/javascript" >     
                        $('#IsNew-checkbox').click(function () {    
                            if ($('#IsNew-checkbox').is(':checked')) {    
                                $('#IsNew').val('true');    
                            } else {    
                                $('#IsNew').val('false');    
                             }    
                        });   
                      </script>  
                      

                      您可以在此处找到更多信息: http://www.blog.mieten.pl/2010/12/asp-net-mvc-custom-checkbox-as-solution-of-string-was-not-recognized-as-a-valid-boolean/

                      【讨论】:

                      • 您不需要为此使用任何 Javascript ... var isChecked = formData[key].Contains("true");
                      【解决方案21】:

                      我遇到了几乎相同的问题,但是我的控制器的返回值被其他值阻止了。

                      找到了一个简单的解决方案,但似乎有点粗糙。

                      尝试在你的控制器中输入Viewbag.,现在你给它一个名字,比如Viewbag.Checkbool

                      现在切换到视图并尝试@Viewbag.Checkbool,您将获得控制器的值。

                      我的控制器参数如下所示:

                      public ActionResult Anzeigen(int productid = 90, bool islive = true)
                      

                      我的复选框会像这样更新:

                      <input id="isLive" type="checkbox" checked="@ViewBag.Value" ONCLICK="window.location.href = '/MixCategory/Anzeigen?isLive=' + isLive.checked.toString()" />
                      

                      【讨论】:

                        【解决方案22】:

                        使用 @mmacaulay ,我想出了这个 bool:

                        // MVC Work around for checkboxes.
                        bool active = (Request.Form["active"] == "on");
                        

                        如果选中 活跃=真

                        如果未选中 活跃=假

                        【讨论】:

                          猜你喜欢
                          • 2019-02-14
                          • 2010-12-20
                          • 2011-01-15
                          • 1970-01-01
                          • 2011-09-07
                          • 2013-10-05
                          • 2013-08-05
                          • 1970-01-01
                          • 2014-11-20
                          相关资源
                          最近更新 更多