【问题标题】:Checkbox for nullable boolean可空布尔值的复选框
【发布时间】:2011-10-14 13:17:35
【问题描述】:

我的模型有一个必须为空的布尔值

public bool? Foo
{
   get;
   set;
}

所以在我的 Razor cshtml 中我有

@Html.CheckBoxFor(m => m.Foo)

除非那行不通。也不会使用 (bool) 进行转换。如果我这样做了

@Html.CheckBoxFor(m => m.Foo.Value)

这不会产生错误,但在发布并且 foo 设置为 null 时它不会绑定到我的模型。在页面上显示 Foo 并使其绑定到我的模型的最佳方式是什么?

【问题讨论】:

  • 该线程忽略了这个问题,即我必须能够将 null 检测为第三个值。未选中复选框的表单提交和未显示复选框的表单提交是必须考虑的两种不同情况。

标签: asp.net-mvc-3 razor nullable


【解决方案1】:

如果选中 = True 而未选中 = null

型号

public NotNullFoo
{
    get { return this.Foo?? false; }
    set { this.Foo= (value == false ? null : true as bool?); }
}


public bool? Foo
{
   get;
   set;
}

查看

  @Html.CheckBoxFor(x => Model.NotNullFoo })

【讨论】:

    【解决方案2】:

    我可以使用它

    @Html.EditorFor(model => model.Foo) 
    

    然后使用以下内容在 Views/Shared/EditorTemplates/Boolean.cshtml 中创建一个文件:

    @model bool?
    
    @Html.CheckBox("", Model.GetValueOrDefault())
    

    【讨论】:

    • 在使用可为空的布尔类型时,我使用了这个 '''@Html.CheckBoxFor(m => m.Foo.HasValue)'''
    • @GauravKumarArora 您正在为可空的HasValue 属性创建一个复选框,但不是实际的布尔值。如果无论基础值是什么,这将创建一个选中的复选框。如果 nullable 有一个值,它将始终为 true。
    • 这绝对是这个页面非常干净和灵活的解决方案。 +1
    • 这会创建两个输入(当然是一个复选框和一个隐藏的表单元素),它们都具有相同的名称,隐藏的一个是 DOM 树中的后者,将序列化为的实际值复选框,因此您似乎必须使用 javascript 来更改已选中/未选中的隐藏元素的值
    【解决方案3】:

    单选按钮很有用,但它们不允许您取消选择。如果您想要这种行为,请考虑使用下拉/选择。以下代码将生成 SelectList 并成功绑定到可为空的布尔值:

    public static SelectList GetBoolTriState()
    {
        var items = new List<SelectListItem>
        {
            new SelectListItem {Value = "", Text = ""},
            new SelectListItem {Value = "True", Text = "Yes"},
            new SelectListItem {Value = "False", Text = "No"},
        };
    
        return new SelectList(items, "Value", "Text");
    }
    

    【讨论】:

      【解决方案4】:

      扩展方法:

              public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression)
              {
                  return htmlHelper.CheckBoxFor<TModel>(expression, null);
              }
              public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, bool?>> expression, object htmlAttributes)
              {
                  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 htmlHelper.CheckBox(ExpressionHelper.GetExpressionText(expression), isChecked??false ,  htmlAttributes);
              }
      

      【讨论】:

        【解决方案5】:

        上面的所有答案都带有它自己的问题。 IMO 最简单/最干净的方法是创建一个助手

        MVC5 剃须刀

        App_Code/Helpers.cshtml

        @helper CheckBoxFor(WebViewPage page, string propertyName, bool? value, string htmlAttributes = null)
        {
            if (value == null)
            {
                <div class="checkbox-nullable">
                    <input type="checkbox" @page.Html.Raw(htmlAttributes)>
                </div>
            }
            else if (value == true)
            {
                <input type="checkbox" value="true" @page.Html.Raw(htmlAttributes) checked>
            }
            else
            {
                <input type="checkbox" value="false" @page.Html.Raw(htmlAttributes)>
            }
        }
        

        用法

        @Helpers.CheckBoxFor(this, "IsPaymentRecordOk", Model.IsPaymentRecordOk)
        

        在我的场景中,可为空的复选框意味着工作人员尚未向客户提出问题,因此它被包裹在 .checkbox-nullable 中,以便您可以适当地设置样式并帮助最终用户确定它既不是truefalse

        CSS

        .checkbox-nullable {
            border: 1px solid red;
            padding: 3px;
            display: inline-block;
        }
        

        【讨论】:

          【解决方案6】:

          我能想到的最简洁的方法是扩展HtmlHelper 可用的扩展,同时仍然重用框架提供的功能。

          public static MvcHtmlString CheckBoxFor<T>(this HtmlHelper<T> htmlHelper, Expression<Func<T, bool?>> expression, IDictionary<string, object> htmlAttributes) {
          
              ModelMetadata modelMeta = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
              bool? value = (modelMeta.Model as bool?);
          
              string name = ExpressionHelper.GetExpressionText(expression);
          
              return htmlHelper.CheckBox(name, value ?? false, htmlAttributes);
          }
          

          我尝试“塑造”表达式以允许直接传递到原生 CheckBoxFor&lt;Expression&lt;Func&lt;T, bool&gt;&gt;&gt;,但我认为这是不可能的。

          【讨论】:

          • 我必须更改方法的签名才能为我工作:我使用了public static MvcHtmlString CheckBoxFor&lt;TModel&gt;(this HtmlHelper&lt;TModel&gt; htmlHelper, Expression&lt;Func&lt;TModel, bool?&gt;&gt; expression, object htmlAttributes),效果非常好。
          【解决方案7】:

          为包含可为空的布尔值的模型制作 EditorTemplate 时...

          • 将可为空的布尔值拆分为 2 个布尔值:

            // Foo is still a nullable boolean.
            public bool? Foo 
            {
                get 
                { 
                    if (FooIsNull)
                        return null;
            
                    return FooCheckbox;  
                }
                set
                {
                    FooIsNull   = (value == null);
                    FooCheckbox = (value ?? false);
                }
            }
            
            // These contain the value of Foo. Public only so they are visible in Razor views.
            public bool FooIsNull { get; set; }
            public bool FooCheckbox { get; set; }
            
          • 在编辑器模板中:

            @Html.HiddenFor(m => m.FooIsNull)
            
            @if (Model.FooIsNull)
            {
                // Null means "checkbox is hidden"
                @Html.HiddenFor(m => m.FooCheckbox)
            }
            else
            {
                @Html.CheckBoxFor(m => m.FooCheckbox)
            }
            
          • 不要回发原始属性 Foo,因为它现在是根据 FooIsNull 和 FooCheckbox 计算的。

          【讨论】:

            【解决方案8】:

            我实际上会为它创建一个模板并将该模板与 EditorFor() 一起使用。

            我是这样做的:

            1. 创建我的模板,它基本上是我在 EditorTemplates 目录中创建的部分视图,在 Shared 下,在 Views 下将其命名为(例如):CheckboxTemplate

              @using System.Globalization    
              @using System.Web.Mvc.Html    
              @model bool?
              
              @{
                  bool? myModel = false;
                  if (Model.HasValue)
                  {
                      myModel = Model.Value;
                  }
              }
              
              <input type="checkbox" checked="@(myModel)" name="@ViewData.TemplateInfo.HtmlFieldPrefix" value="True" style="width:20px;" />
              
            2. 像这样使用它(在任何视图中):

              @Html.EditorFor(x => x.MyNullableBooleanCheckboxToBe, "CheckboxTemplate")

            就是这样。

            模板在 MVC 中非常强大,请使用它们。您可以将整个页面创建为模板,您可以将其与 @Html.EditorFor() 一起使用;前提是您在 lambda 表达式中传递其视图模型..

            【讨论】:

              【解决方案9】:
              @{  bool testVar = ((bool)item.testVar ? true : false); }
                @Html.DisplayFor(modelItem => testVar)
              

              【讨论】:

              • 如果 testVar 为 null,则会抛出异常。
              【解决方案10】:

              在类似问题中找到答案 - Rendering Nullable Bool as CheckBox。 这很简单,而且很有效:

              @Html.CheckBox("RFP.DatesFlexible", Model.RFP.DatesFlexible ?? false)
              @Html.Label("RFP.DatesFlexible", "My Dates are Flexible")
              

              这就像 @afinkelstein 接受的答案,只是我们不需要特殊的“编辑器模板”

              【讨论】:

              • 与已接受的答案相比,此方法的另一个优点是,您可以根据应用程序的逻辑,根据具体情况决定 null 是否应等于 true 或 false,而不是使用单个默认值在整个应用程序中(或制作两个编辑器模板!)。
              • 清洁解决方案就像一个魅力。第一行是关键
              • 这就是我最终要做的,因为它的意图很明确。我要做的唯一更改是使用nameofModel.RFP.DatesFlexible 周围的类似内容而不是硬编码字符串,以从静态类型/智能感知/ReSharper 重构/等中受益。
              【解决方案11】:

              我在模型中有bool? IsDisabled { get; set; }。如果在视图中,则插入。

              <div class="inputClass" id="disabled">
                  <div>
                  @if(Model.IsDisabled==null)
                  {
                      Model.IsDisabled = false;
                  }           
                  @Html.CheckBoxFor(model => model.IsDisabled.Value)         
                  </div>
              </div> 
              

              【讨论】:

              • 不是很好,因为您更改了模型中的值,并且在许多情况下,false 不等于 null。我给了你一点,因为这是一个很好的尝试。请注意,我也同意达林 :)
              • 建议不要给出分数,因为这是一个很好的尝试,因为它可能会错误地向其他读者表明答案在出现问题时有用
              • 一个更好看的替代方法是在渲染页面之前在控制器中将布尔值设置为 false if null,但仍然使用 .Value 的想法。
              【解决方案12】:

              我也遇到了同样的问题。我尝试了以下方法来解决问题 因为我不想更改数据库并再次生成 EDMX。

              @{
              
                 bool testVar = (Model.MYVar ? true : false);
              
               }
              
              <label>@Html.CheckBoxFor(m => testVar)testVar</label><br />
              

              【讨论】:

                【解决方案13】:

                只检查空值并返回false:

                @{ bool nullableValue = ((Model.nullableValue == null) || (Model.nullableValue == false)) ? false : true; }
                @Html.CheckBoxFor(model => nullableValue)
                

                【讨论】:

                • 您知道您的属性元数据会丢失,对吧?客户端验证,字段名和id等
                【解决方案14】:

                复选框只为您提供 2 个值(真、假)。可空的布尔值有 3 个值(真、假、空),所以不可能用复选框来实现。

                一个不错的选择是使用下拉菜单。

                型号

                public bool? myValue;
                public List<SelectListItem> valueList;
                

                控制器

                model.valueList = new List<SelectListItem>();
                model.valueList.Add(new SelectListItem() { Text = "", Value = "" });
                model.valueList.Add(new SelectListItem() { Text = "Yes", Value = "true" });
                model.valueList.Add(new SelectListItem() { Text = "No", Value = "false" });
                

                查看

                @Html.DropDownListFor(m => m.myValue, valueList)
                

                【讨论】:

                • 如果您不关心用户能够设置所有 3 个可能的值,那么您不能仅使用一个复选框来表示所有 3 个值。
                • @Dave 我认为你完全没有抓住重点。 OP有一个布尔值?这意味着他需要选择 3 个可能的值之一。如果他只想有真或假,他应该使用布尔而不是布尔?
                • 实际上,OP的评论表明相反 - 如果表单中有一个复选框,他根本不希望 null 成为一个选项,因为当有不同的选项时会覆盖该选项没有复选框的表单。
                • 我在搜索页面中使用了同样的策略。您可以搜索是、否或忽略搜索中的布尔值。 一个用例! :D
                【解决方案15】:

                这是一个老问题,现有答案描述了大多数替代方案。但是有一个简单的选择,如果你有 bool?在您的视图模型中,并且您不关心 UI 中的 null:

                @Html.CheckBoxFor(m => m.boolValue ?? false);
                

                【讨论】:

                • 你试过了吗?由于 xxxFor 方法需要一个成员表达式,我敢打赌这会产生运行时失败。它正在尝试确定目标属性或字段,此时不评估任何内容。
                • 运行时错误“模板只能用于字段访问、属性访问、单维数组索引或单参数自定义索引器表达式。”
                【解决方案16】:

                对我来说,解决方案是更改视图模型。考虑您正在搜索发票。这些发票可以支付或不支付。因此,您的搜索有三个选项:付费、无偿或“我不在乎”。

                我最初将其设置为布尔值?字段:

                public bool? PaidInvoices { get; set; }
                

                这让我偶然发现了这个问题。我最终创建了一个 Enum 类型,并按如下方式处理:

                @Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.NotSpecified, new { @checked = true })
                @Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.Yes) 
                @Html.RadioButtonFor(m => m.PaidInvoices, PaidStatus.No)
                

                当然,我将它们包裹在标签中并指定了文本,我只是说这里还有一个要考虑的选项。

                【讨论】:

                • 这是一个有用的表示。如果您的空间有限,您也可以通过下拉/选择来实现此目的,只是需要单击两次才能选择一个选项。与选择一样,单选按钮在不同浏览器之间看起来可能非常不同,这可能是设计者的地狱。
                【解决方案17】:

                我过去也遇到过类似的问题。

                在 HTML 中创建一个复选框输入,并设置属性 name="Foo" 这应该仍然可以正确发布。

                <input type="checkbox" name="Foo" checked="@model.Foo.Value" /> Foo Checkbox<br />
                

                【讨论】:

                • 这似乎没有发布,我仍然为空。
                • 这可能与模型绑定有问题
                【解决方案18】:

                不推荐使用带有隐藏字段的原语来阐明是 False 还是 Null。

                复选框不是你应该使用的——它实际上只有一种状态:选中。否则,它可能是任何东西。

                当您的数据库字段为可空布尔值 (bool?) 时,UX 应使用 3 单选按钮,其中第一个按钮代表您的“已选中”,第二个按钮代表“未选中”,第三个按钮代表您的null,无论 null 的语义是什么意思。您可以使用&lt;select&gt;&lt;option&gt; 下拉列表来节省空间,但用户必须单击两次,并且选择不会立即清晰。

                  1     0      null 
                True  False  Not Set
                Yes   No     Undecided
                Male  Female Unknown
                On    Off    Not Detected
                

                RadioButtonList,定义为一个名为 RadioButtonForSelectList 的扩展,为您构建单选按钮,包括选中/选中的值,并设置 &lt;div class="RBxxxx"&gt;,以便您可以使用 css 使您的单选按钮变为水平状态(显示:inline-块)、垂直或表格形式(显示:inline-block;宽度:100px;)

                在模型中(我使用字符串,字符串作为字典定义作为教学示例。你可以使用 bool?,字符串)

                public IEnumerable<SelectListItem> Sexsli { get; set; }
                       SexDict = new Dictionary<string, string>()
                        {
                                { "M", "Male"},
                                { "F", "Female" },
                                { "U", "Undecided" },
                
                        };
                
                        //Convert the Dictionary Type into a SelectListItem Type
                        Sexsli = SexDict.Select(k =>
                              new SelectListItem
                              {
                                  Selected = (k.Key == "U"),
                                  Text = k.Value,
                                  Value = k.Key.ToString()
                              });
                
                <fieldset id="Gender">
                <legend id="GenderLegend" title="Gender - Sex">I am a</legend>
                    @Html.RadioButtonForSelectList(m => m.Sexsli, Model.Sexsli, "Sex") 
                        @Html.ValidationMessageFor(m => m.Sexsli)
                </fieldset>
                
                public static class HtmlExtensions
                {
                public static MvcHtmlString RadioButtonForSelectList<TModel, TProperty>(
                    this HtmlHelper<TModel> htmlHelper,
                    Expression<Func<TModel, TProperty>> expression,
                    IEnumerable<SelectListItem> listOfValues,
                    String rbClassName = "Horizontal")
                {
                var metaData = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
                var sb = new StringBuilder();
                
                if (listOfValues != null)
                {
                    // Create a radio button for each item in the list 
                    foreach (SelectListItem item in listOfValues)
                    {
                        // Generate an id to be given to the radio button field 
                        var id = string.Format("{0}_{1}", metaData.PropertyName, item.Value);
                
                        // Create and populate a radio button using the existing html helpers 
                        var label = htmlHelper.Label(id, HttpUtility.HtmlEncode(item.Text));
                
                        var radio = String.Empty;
                
                        if (item.Selected == true)
                        {
                            radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id, @checked = "checked" }).ToHtmlString();
                        }
                        else
                        {
                            radio = htmlHelper.RadioButtonFor(expression, item.Value, new { id = id }).ToHtmlString();
                
                        }// Create the html string to return to client browser
                        // e.g. <input data-val="true" data-val-required="You must select an option" id="RB_1" name="RB" type="radio" value="1" /><label for="RB_1">Choice 1</label> 
                
                        sb.AppendFormat("<div class=\"RB{2}\">{0}{1}</div>", radio, label, rbClassName);
                    }
                }
                
                return MvcHtmlString.Create(sb.ToString());
                }
                }
                

                【讨论】:

                  【解决方案19】:

                  我的模型有一个必须为空的布尔值

                  为什么?这没有意义。复选框有两种状态:选中/未选中,或者如果您愿意,则为 True/False。没有第三种状态。

                  或者等待您在视图中使用域模型而不是视图模型?那是你的问题。所以我的解决方案是使用一个视图模型,您将在其中定义一个简单的布尔属性:

                  public class MyViewModel
                  {
                      public bool Foo { get; set; }
                  }
                  

                  现在您将让控制器操作将此视图模型传递给视图并生成正确的复选框。

                  【讨论】:

                  • foo 输入可能根本不会出现在页面上的原因有很多。如果确实出现了,我可以假设有一个值,但我需要能够区分何时发布模型且 foo 未选中和未显示 foo。
                  • @KMulligan,如何在您的视图模型中包含另一个布尔属性,指示您是否应该为 Foo 属性呈现复选框,并且可以将其存储在隐藏字段中,以便在回发时知道是否您是否应该考虑 Foo 值。
                  • 我最初是这样做的,但是由于各种原因,我得到了大量这些原始值类型,它们可能包含在我的表单中,也可能不包含在我的表单中。为所有人添加布尔值开始感觉很愚蠢。当我在谷歌上搜索 nullables 时,我认为这是要走的路。
                  • @KMulligan,您可以编写帮助程序、编辑器模板……这样您就不必为每个具有此类属性的属性重复此操作。
                  • @DarinDimitrov,您明显错过或忽略了这样一个事实,即 OP 说他必须能够表示布尔的“空”方面。你挂断了他想使用 CheckBoxFor 的事实。与其告诉他他写了“糟糕的代码”,因为他使用了他的域模型(他可能会或可能不会这样做)并告诉他复选框被选中或未选中,您应该尝试回答他的问题或不回答。
                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2012-06-04
                  • 1970-01-01
                  • 2019-02-23
                  • 1970-01-01
                  • 2013-12-09
                  • 2015-11-19
                  相关资源
                  最近更新 更多