Checkbox for nullable boolean

asp.net Mvc-3RazorNullable

asp.net Mvc-3 Problem Overview


My model has a boolean that has to be nullable

public bool? Foo
{
   get;
   set;
}

so in my Razor cshtml I have

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

except that doesn't work. Neither does casting it with (bool). If I do

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

that doesn't create an error, but it doesn't bind to my model when posted and foo is set to null. Whats the best way to display Foo on the page and make it bind to my model on a post?

asp.net Mvc-3 Solutions


Solution 1 - asp.net Mvc-3

I got it to work with

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

and then making a file at Views/Shared/EditorTemplates/Boolean.cshtml with the following:

@model bool?

@Html.CheckBox("", Model.GetValueOrDefault())

Solution 2 - asp.net Mvc-3

Found answer in similar question - https://stackoverflow.com/q/8484127/1193727. It's very straightforward and just works:

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

It's like accepted answer from @afinkelstein except we don't need special 'editor template'

Solution 3 - asp.net Mvc-3

I have bool? IsDisabled { get; set; } in Model. Inserted if in View.

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

Solution 4 - asp.net Mvc-3

> My model has a boolean that has to be nullable

Why? This doesn't make sense. A checkbox has two states: checked/unchecked, or True/False if you will. There is no third state.

Or wait you are using your domain models in your views instead of view models? That's your problem. So the solution for me is to use a view model in which you will define a simple boolean property:

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

and now you will have your controller action pass this view model to the view and generate the proper checkbox.

Solution 5 - asp.net Mvc-3

Complicating a primitive with hidden fields to clarify whether False or Null is not recommended.

Checkbox isn't what you should be using -- it really only has one state: Checked. Otherwise, it could be anything.

When your database field is a nullable boolean (bool?), the UX should use 3-Radio Buttons, where the first button represents your "Checked", the second button represents "Not Checked" and the third button represents your null, whatever the semantics of null means. You could use a <select><option> drop down list to save real estate, but the user has to click twice and the choices aren't nearly as instantaneously clear.

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

The RadioButtonList, defined as an extension named RadioButtonForSelectList, builds the radio buttons for you, including the selected/checked value, and sets the <div class="RBxxxx"> so you can use css to make your radio buttons go horizontal (display: inline-block), vertical, or in a table fashion (display: inline-block; width:100px;)

In the model (I'm using string, string for the dictionary definition as a pedagogical example. You can use bool?, string)

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());
}
}

Solution 6 - asp.net Mvc-3

For me the solution was to change the view model. Consider you are searching for invoices. These invoices can be paid or not. So your search has three options: Paid, Unpaid, or "I don't Care".

I had this originally set as a bool? field:

public bool? PaidInvoices { get; set; }

This made me stumble onto this question. I ended up created an Enum type and I handled this as follows:

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

Of course I had them wrapped in labels and had text specified, I just mean here's another option to consider.

Solution 7 - asp.net Mvc-3

Checkbox only offer you 2 values (true, false). Nullable boolean has 3 values (true, false, null) so it's impossible to do it with a checkbox.

A good option is to use a drop down instead.

Model

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

Controller

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" });

View

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

Solution 8 - asp.net Mvc-3

I would actually create a template for it and use that template with an EditorFor().

Here is how I did it:

  1. Create My template, which is basically a partial view I created in the EditorTemplates directory, under Shared, under Views name it as (for example): 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. Use it like this (in any view):

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

Thats all.

Templates are so powerful in MVC, use them.. You can create an entire page as a template, which you would use with the @Html.EditorFor(); provided that you pass its view model in the lambda expression..

Solution 9 - asp.net Mvc-3

The cleanest approach I could come up with is to expand the extensions available to HtmlHelper while still reusing functionality provided by the framework.

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);
}

I experimented with 'shaping' the expression to allow a straight pass through to the native CheckBoxFor<Expression<Func<T, bool>>> but I don't think it's possible.

Solution 10 - asp.net Mvc-3

I had a similar issue in the past.

Create a Checkbox input in HTML, and set the attribute name="Foo" This should still post properly.

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

Solution 11 - asp.net Mvc-3

Just check for the null value and return false to it:

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

Solution 12 - asp.net Mvc-3

I also faced the same issue. I tried the following approach to solve the issue because i don't want to change the DB and again generate the EDMX.

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

Solution 13 - asp.net Mvc-3

When making an EditorTemplate for a model which contains a nullable bool...

  • Split the nullable bool into 2 booleans:

      // 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; }
    
  • Within the editor template:

      @Html.HiddenFor(m => m.FooIsNull)
    
      @if (Model.FooIsNull)
      {
          // Null means "checkbox is hidden"
          @Html.HiddenFor(m => m.FooCheckbox)
      }
      else
      {
          @Html.CheckBoxFor(m => m.FooCheckbox)
      }
    
  • Do not postback the original property Foo, because that is now calculated from FooIsNull and FooCheckbox.

Solution 14 - asp.net Mvc-3

All the answers above came with it's own issues. Easiest/cleanest way IMO is to create a helper

MVC5 Razor

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)>
    }
}

Usage

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

In my scenario, a nullable checkbox means that a staff member had not yet asked the question to the client, so it's wrapped in a .checkbox-nullable so that you may style appropriately and help the end-user identify that it is neither true nor false

CSS

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

Solution 15 - asp.net Mvc-3

Extension methods:

        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);
        }

Solution 16 - asp.net Mvc-3

Radio buttons are useful, but they don't allow you to deselect. If you want this behaviour, then consider using a drop-down/select. The following code will generate the SelectList and this binds successfully to a nullable boolean:

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");
}

Solution 17 - asp.net Mvc-3

If checked = True and not checked = null

Model

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


public bool? Foo
{
   get;
   set;
}

View

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

Solution 18 - asp.net Mvc-3

@{  bool testVar = ((bool)item.testVar ? true : false); }
  @Html.DisplayFor(modelItem => testVar)

Solution 19 - asp.net Mvc-3

This is an old question, and the existing answers describe most of the alternatives. But there's one simple option, if you have bool? in your viewmodel, and you don't care about null in your UI:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDMulliganView Question on Stackoverflow
Solution 1 - asp.net Mvc-3DMulliganView Answer on Stackoverflow
Solution 2 - asp.net Mvc-3resnyanskiyView Answer on Stackoverflow
Solution 3 - asp.net Mvc-3VitalyBView Answer on Stackoverflow
Solution 4 - asp.net Mvc-3Darin DimitrovView Answer on Stackoverflow
Solution 5 - asp.net Mvc-3Jules BartowView Answer on Stackoverflow
Solution 6 - asp.net Mvc-3PaulView Answer on Stackoverflow
Solution 7 - asp.net Mvc-3GudradainView Answer on Stackoverflow
Solution 8 - asp.net Mvc-3t_plusplusView Answer on Stackoverflow
Solution 9 - asp.net Mvc-3Red TazView Answer on Stackoverflow
Solution 10 - asp.net Mvc-3Chris LucianView Answer on Stackoverflow
Solution 11 - asp.net Mvc-3Rodrigo BorattoView Answer on Stackoverflow
Solution 12 - asp.net Mvc-3Amit KumarView Answer on Stackoverflow
Solution 13 - asp.net Mvc-3user1023602View Answer on Stackoverflow
Solution 14 - asp.net Mvc-3zanderwarView Answer on Stackoverflow
Solution 15 - asp.net Mvc-3JimView Answer on Stackoverflow
Solution 16 - asp.net Mvc-3SavageView Answer on Stackoverflow
Solution 17 - asp.net Mvc-3RuiView Answer on Stackoverflow
Solution 18 - asp.net Mvc-3edrdesignerView Answer on Stackoverflow
Solution 19 - asp.net Mvc-3Jeff DegeView Answer on Stackoverflow