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.

Advertisements

2 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!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s