How do I display the DisplayAttribute.Description attribute value?

Solution 1:

I ended up with a helper like this:

using System;
using System.Linq.Expressions;
using System.Web.Mvc;

public static class MvcHtmlHelpers
{
    public static MvcHtmlString DescriptionFor<TModel, TValue>(this HtmlHelper<TModel> self, Expression<Func<TModel, TValue>> expression)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, self.ViewData);
        var description = metadata.Description;

        return MvcHtmlString.Create(string.Format(@"<span>{0}</span>", description));
    }
}

Thanks to those who led me in the right direction. :)

Solution 2:

Using the technique from this article about how to Display visual hints for the fields in your form, you can access the value via the following:

@Html.TextBoxFor( 
        model => model.Email , 
        new { title = ModelMetadata.FromLambdaExpression<RegisterModel , string>( 
            model => model.Email , ViewData ).Description } )  

Solution 3:

I was about to use the accepted answer, but it didn't work for ASP.NET Core 1/2 (a.k.a. MVC 6) because ModelMetadata.FromLambdaExpression no longer exists and has been moved to ExpressionMetadataProvider (also usage has been changed slightly).

This is an updated extension method you can use with ASP.NET Core 1.1 & 2:

using System;
using System.Linq.Expressions;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;

public static class HtmlExtensions
{
    public static IHtmlContent DescriptionFor<TModel, TValue>(this IHtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
    {
        if (html == null) throw new ArgumentNullException(nameof(html));
        if (expression == null) throw new ArgumentNullException(nameof(expression));

        var modelExplorer = ExpressionMetadataProvider.FromLambdaExpression(expression, html.ViewData, html.MetadataProvider);
        if (modelExplorer == null) throw new InvalidOperationException($"Failed to get model explorer for {ExpressionHelper.GetExpressionText(expression)}");

        return new HtmlString(modelExplorer.Metadata.Description);
    }
}

ASP.NET Core 1

For ASP.NET Core 1, the same code works, but you'll need different namespace usings:

using System;
using System.Linq.Expressions;
using Microsoft.AspNet.Html.Abstractions;
using Microsoft.AspNet.Mvc.ViewFeatures;

Usage

@Html.DescriptionFor(model => model.Phone1)

Solution 4:

In ASP.NET MVC Core you can use the new Tag Helpers, that makes your HTML look like... HTML :)

Like this:

<div class="form-group row">
    <label asp-for="Name" class="col-md-2 form-control-label"></label>
    <div class="col-md-10">
        <input asp-for="Name" class="form-control" aria-describedby="Name-description" />
        <span asp-description-for="Name" class="form-text text-muted" />
        <span asp-validation-for="Name" class="text-danger" />
    </div>
</div>

Note 1: You can use the aria-describedby attribute in the input element as that id will be created automatically in the span element with asp-description-for attribute.

Note 2: In Bootstrap 4, the classes form-text and text-muted replaces the v3 help-block class for block-level help text.

For this magic to happen, you just need to create a new Tag Helper:

using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.TagHelpers;

/// <summary>
/// <see cref="ITagHelper"/> implementation targeting &lt;span&gt; elements with an <c>asp-description-for</c> attribute.
/// Adds an <c>id</c> attribute and sets the content of the &lt;span&gt; with the Description property from the model data annotation DisplayAttribute.
/// </summary>
[HtmlTargetElement("span", Attributes = DescriptionForAttributeName)]
public class SpanDescriptionTagHelper : TagHelper
{
    private const string DescriptionForAttributeName = "asp-description-for";

    /// <summary>
    /// Creates a new <see cref="SpanDescriptionTagHelper"/>.
    /// </summary>
    /// <param name="generator">The <see cref="IHtmlGenerator"/>.</param>
    public SpanDescriptionTagHelper(IHtmlGenerator generator)
    {
        Generator = generator;
    }

    /// <inheritdoc />
    public override int Order
    {
        get
        {
            return -1000;
        }
    }

    [HtmlAttributeNotBound]
    [ViewContext]
    public ViewContext ViewContext { get; set; }

    protected IHtmlGenerator Generator { get; }

    /// <summary>
    /// An expression to be evaluated against the current model.
    /// </summary>
    [HtmlAttributeName(DescriptionForAttributeName)]
    public ModelExpression DescriptionFor { get; set; }

    /// <inheritdoc />
    /// <remarks>Does nothing if <see cref="DescriptionFor"/> is <c>null</c>.</remarks>
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (output == null)
        {
            throw new ArgumentNullException(nameof(output));
        }

        var metadata = DescriptionFor.Metadata;

        if (metadata == null)
        {
            throw new InvalidOperationException(string.Format("No provided metadata ({0})", DescriptionForAttributeName));
        }

        output.Attributes.SetAttribute("id", metadata.PropertyName + "-description");

        if( !string.IsNullOrWhiteSpace( metadata.Description))
        {
            output.Content.SetContent(metadata.Description);
            output.TagMode = TagMode.StartTagAndEndTag;
        }
    }
}

And make your Tag Helpers available to all our Razor views. Add the addTagHelper directive to the Views/_ViewImports.cshtml file:

@addTagHelper "*, YourAssemblyName"

Note 1: Replace YourAssemblyName with the assembly name of your project.

Note 2: You just need to do this once, for all your Tag Helpers!

More information on Tag Helpers here: https://docs.asp.net/en/latest/mvc/views/tag-helpers/intro.html

That’s it! Have fun with the new Tag Helpers!

Solution 5:

If anyone is wondering how to use the accepted answer

1- In your solution explorer > Add new folder > name it"Helpers" for example
2- Add a new class, name it "CustomHtmlHelpers" for example
3- Paste the code :

public static class MvcHtmlHelpers
{
    public static string DescriptionFor<TModel, TValue>(this HtmlHelper<TModel> self, Expression<Func<TModel, TValue>> expression)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, self.ViewData);
        var description = metadata.Description;

        return string.IsNullOrWhiteSpace(description) ? "" : description;
    }
}

4- In your model or viewModel using it this:

[Display(Name = "User Name", Description = "Enter your User Name")]
public string FullName { get; set; }

5- In your Razor view, after the @model, type this line

@using YOUR_PROJECT.Helpers 

6- Display the description like this:

@Html.DescriptionFor(m => m.FullName) 

7- You may want to use the description to display text in the input placeholder:

@Html.DisplayNameFor(m => m.FullName)
@Html.TextBoxFor(m => m.FullName, new { @class = "form-control", placeholder = Html.DescriptionFor(m => m.FullName) })

Thanks