Asp.Net MVC – Nicer display for boolean values

How can we get Asp.Net MVC to display boolean values better?

Lets have a simple model:

public class Options
{
    [Display(Name = "Flux")]
    public bool EnableFlux { get; set; }

    [Display(Name = "Advanced Options")]
    public bool ShowAdvancedOptions { get; set; }

    [Display(Name = "Do you eat well?")]
    public bool UserEatsWell { get; set; }
}

The default display template isn’t great – it just displays disabled checkboxes:

<h4>Display:</h4>
@Html.LabelFor(m => m.ShowAdvancedOptions)
@Html.DisplayFor(m => m.ShowAdvancedOptions)

@Html.LabelFor(m => m.EnableFlux)
@Html.DisplayFor(m => m.EnableFlux)

@Html.LabelFor(m => m.UserEatsWell)
@Html.DisplayFor(m => m.UserEatsWell)

The result isn’t too pretty:
Mvc Booleans - default

What I’d like is to have a representative text instead of that poor checkbox. We want different text for different boolean fields: Enable/Disable, Yes/No, Show/Hide, etc.
Luckily, this is quite simple with MVC.

First, since we are using data annotation, that gives us a good place to start. We’ll write an attribute that specifies for each field its display values:

/// <summary>
/// For a boolean field, set the display text for <c>true</c> and
/// <c>false</c> values.
/// </summary>
[AttributeUsage(AttributeTargets.Property,
                Inherited = false, AllowMultiple = false)]
public class BooleanDisplayValuesAttribute : Attribute, IMetadataAware
{
    public const string TrueTitleAdditionalValueName = "BooleanTrueValueTitle";
    public const string FalseTitleAdditionalValueName = "BooleanFalseValueTitle";

    private readonly string _trueValueTitle;
    private readonly string _falseValueTitle;

    public BooleanDisplayValuesAttribute(string trueValueTitle,
                                         string falseValueTitle)
    {
        _trueValueTitle = trueValueTitle;
        _falseValueTitle = falseValueTitle;
    }

    public void OnMetadataCreated(ModelMetadata metadata)
    {
        metadata.AdditionalValues[TrueTitleAdditionalValueName] = _trueValueTitle;
        metadata.AdditionalValues[FalseTitleAdditionalValueName] = _falseValueTitle;
    }
}

We’ve create an attribute that implements the IMetadataAware interface, so it can add values to ModelMetadata.AdditionalValues.

We’ll also throw in three useful derived classes:

/// <summary>
/// For a boolean field, set the display text for <c>true</c> and
/// <c>false</c> values to "Show" and "Hide".
/// </summary>
public class BooleanDisplayValuesAsShowHideAttribute :
   BooleanDisplayValuesAttribute
{
    public BooleanDisplayValuesAsShowHideAttribute()
        : base("Show", "Hide")
    {
    }
}

/// <summary>
/// For a boolean field, set the display text for <c>true</c> and
/// <c>false</c> values to "Enable" and "Disable".
/// </summary>
public class BooleanDisplayValuesAsEnableDisableAttribute :
    BooleanDisplayValuesAttribute
{
    public BooleanDisplayValuesAsEnableDisableAttribute()
        : base("Enable", "Disable")
    {
    }
}

/// <summary>
/// For a boolean field, set the display text for <c>true</c> and
/// <c>false</c> values to "Yes" and "No".
/// </summary>
public class BooleanDisplayValuesAsYesNoAttribute :
    BooleanDisplayValuesAttribute
{
    public BooleanDisplayValuesAsYesNoAttribute()
        : base("Yes", "No")
    {
    }
}

Now we can decorate our model:

public class Options
{
    [Display(Name = "Flux")]
    [BooleanDisplayValuesAsEnableDisable]
    public bool EnableFlux { get; set; }

    [Display(Name = "Advanced Options")]
    [BooleanDisplayValuesAsShowHide]
    public bool ShowAdvancedOptions { get; set; }

    [Display(Name = "Do you eat well?")]
    [BooleanDisplayValuesAsYesNo]
    public bool UserEatsWell { get; set; }
}

And, the only thing left is to read the values. We’ll write a small HTML Helper:

/// <summary>
/// Return options represnting the True and False titles of a 
/// boolean field.
/// </summary>
/// <returns>A list with the false title at position 0, 
/// and true title at position 1.</returns>
public static IList<SelectListItem> OptionsForBoolean<TModel,
                                                      TProperty>(
    this HtmlHelper<TModel> htmlHelper,
    Expression<Func<TModel, TProperty>> expression)
{
    var metaData = ModelMetadata.FromLambdaExpression(expression,
                                                  htmlHelper.ViewData);
    object trueTitle;
    metaData.AdditionalValues.TryGetValue(
        BooleanDisplayValuesAttribute.TrueTitleAdditionalValueName,
        out trueTitle);
    trueTitle = trueTitle ?? "Yes";

    object falseTitle;
    metaData.AdditionalValues.TryGetValue(
        BooleanDisplayValuesAttribute.FalseTitleAdditionalValueName,
        out falseTitle);
    falseTitle = falseTitle ?? "No";

    var options = new[]
                        {
                            new SelectListItem {Text = (string) falseTitle,
                                                Value = Boolean.FalseString},
                            new SelectListItem {Text = (string) trueTitle,
                                                Value = Boolean.TrueString},
                        };
    return options;
}

Usage

For displaying values, I added a Display Template for all booleans, but you might prefer a UiHint.
At ~/Views/Shared/DisplayTemplates/Boolean.cshtml :

@model Boolean
@{
    var option = Html.OptionsForBoolean(m => m)
                     .Single(o => o.Value == (Model ?
                                              Boolean.TrueString :
                                              Boolean.FalseString));
}
<span data-boolean-value="@Model.ToString().ToLowerInvariant()">@option.Text</span>

Note that I threw in a data attribute, data-boolean-value, with the boolean value formatted for JavaScript. A better alternative may be the <data> element, but it is poorly supported at this time.

Result

The same code as above gives:
Mvc Booleans - Nicer Display

We can also change the edit mode. For example, here I use a drop-down:

@Html.LabelFor(m => m.ShowAdvancedOptions)
@Html.DropDownListFor(m => m.ShowAdvancedOptions,
                      Html.OptionsForBoolean(m => m.ShowAdvancedOptions))

Mvc Booleans - Edit mode using drop downs

With a little extra code, I also wrote nicer edit templates, which show radio buttons, designed as a switch.

7 thoughts on “Asp.Net MVC – Nicer display for boolean values

  1. Thanks a lot for taking the time to write this! Solved a bunch of my checkbox + mvc (hint:hidden input fields) + Formmethod.GET problems!

  2. How this can work on Asp net core 2.1? IMetadataAware Interface is not supported from MVC 6 framework onward?

    • Hello Robert!
      I knew this question would come one day…

      I don’t know. I looked around and didn’t find a direct replacement for this technique, but here are a few pointers I’ve found:

      GitHub – Restore missing `ModelMetadata` properties (additional metadata)
      AdditionalMetadataAttribute Anyone?
      Microsoft Docs – IDisplayMetadataProvider Interface

      I don’t have enough time at the moment to try and implement it, and there may be an entirely new/better approach in Core that I just don’t know, but please let me know if you find anything that works.

      Thanks!

      • Kobi
        Thanks for quick reply. I still haven’t found anything useful and I am not near the level of coding you are and I am not able to replicate all those metadata templates yet.
        Currently I am using:
        1) Edit Mode:
        As a workaround I am just using this code on the edit pages:
        @Html.DropDownListFor(m => m.IsActive, new SelectList(
        new[] {
        new { Value = “”, Text = “– Choose YES or NO –” },
        new { Value = “true”, Text = “YES” },
        new { Value = “false”, Text = “NO” },
        },
        “Value”,
        “Text”
        ))
        Yes, No, Null, text can easily by replaced by other text, but this is not neat and repeatable as your code. I read there is an option somewhere to create DropDownList template, but I did not get to that knowledge yet.

        2) List/Index Mode:
        Here I am using Html.Helpers
        using System;
        using Microsoft.AspNetCore.Html;
        using Microsoft.AspNetCore.Mvc.Rendering;
        namespace PMS.Infrastructure
        {
        public static class HTMLHelpers
        {
        public static IHtmlContent YesNoNothing(this IHtmlHelper htmlHelper, bool? yesNo)
        {
        string s = “”;
        if (yesNo == null) { s = “-“; };
        if (yesNo == true) { s = “Yes”; };
        if (yesNo == false) { s = “No”; };
        return new HtmlString(s);
        }
        }}

        3) This helper file is registered in _ViewImports.cshtml in Shared folder:
        Enables the Xbuilt-in tag helpers, which I can use to create HTML elements that reflect the configuation of the application
        @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
        Enables html tag helper
        @using PMS.Infrastructure

        4) and in Index.cshtml page I am using this code:
        @Html.YesNoNothing(item.IsActive)

        That is the only current workaround I have found, using brute force and basic coding, but it works.
        I wish I could improve it and make it repeatable as your code was in MVC 5.

Leave a reply to Andrei Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.