17

How do I get the [Display(Name="Some Title")] DataAnnotations "Some Title" rendered in the List scaffold view's output?

I create a strongly typed list scaffold view for this class:

public class CompanyHoliday
{
    [Key]
    public int Id { get; set; }

    [Required]
    [Display(Name = "Datum")]
    [DataType(System.ComponentModel.DataAnnotations.DataType.Date)]
    public DateTime Date { get; set; }

    [Required]
    [StringLength(50)]
    [Display(Name = "Feiertag Name")]
    public string Name { get; set; }
}

The view looks like this:

@model IEnumerable<Zeiterfassung.Domain.CompanyHoliday>

@{
    ViewBag.Title = "Year";
}

<h2>Year</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
    <th></th>
    <th>
        Date
    </th>
    <th>
        Name
    </th>
</tr>

@foreach (var item in Model) {
<tr>
    <td>
        @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
        @Html.ActionLink("Details", "Details", new { id=item.Id }) |
        @Html.ActionLink("Delete", "Delete", new { id=item.Id })
    </td>
    <td>
        @String.Format("{0:g}", item.Date)
    </td>
    <td>
        @item.Name
    </td>
</tr>
}

</table>

However, I don't want "Date" and "Name" in the table header. I want "Datum" and "Feiertag Name" rendered there dynamically.

The actual column header titles should come from the Display DataAnnotation.

How do I do this?

ataravati
  • 8,891
  • 9
  • 57
  • 89
toebens
  • 4,039
  • 6
  • 24
  • 31
  • 3
    This seems like it would be extremely common, as everyone who uses MvcScaffolding should have this same issue, and the table header name based on the property name that scaffolding puts there is never useful in any 'real' corporate/production application (or anything that supports more than 1 language). I'm amazed there aren't more answers... – CodingWithSpike Nov 19 '11 at 14:06

3 Answers3

20

Bit of an old topic, but I came up with an extension method to handle this.

Lets say my model is:

public class MyModel
{
    [Display(Name = "Some Property")]
    public string SomeProp { get; set; }
}

Drop this method into a static class:

namespace Web.Extensions
{
    public static class HtmlHelperExtensions
    {
        public static MvcHtmlString DisplayNameFor<TModel, TProperty>(this HtmlHelper<IEnumerable<TModel>> helper, Expression<Func<TModel, TProperty>> expression)
        {
            var name = ExpressionHelper.GetExpressionText(expression);
            name = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
            var metadata = ModelMetadataProviders.Current.GetMetadataForProperty(() => Activator.CreateInstance<TModel>(), typeof(TModel), name);
            return new MvcHtmlString(metadata.DisplayName);
        }
    } 
}

Then in your View, which is taking an IEnumerable<MyModel>, you can use it like this:

@using Web.Extensions
@model IEnumerable<MyModel>

<table>
    <tr>
        <th>
            @Html.DisplayNameFor(m => m.AvgElecCost)
        </th>

The generated HTML will be:

        <th>
            Some Property
        </th>

Hope it helps!

CodingWithSpike
  • 42,906
  • 18
  • 101
  • 138
  • 2
    Awesome, I'm going to add this extension to my toolbox! – kingdango Nov 19 '11 at 22:32
  • 4
    I added the entire file (which also contained a couple more methods) to a Gist: https://gist.github.com/1379767#file_html_helper_display_name_extensions.cs Enjoy! – CodingWithSpike Nov 20 '11 at 03:31
  • 2
    +1 - Indeed, it seems that this overload is now used by the standard scaffolding in [MVC 4](http://msdn.microsoft.com/en-us/library/system.web.mvc.html.displaynameextensions(v=vs.108).aspx). Example [here](http://stackoverflow.com/questions/4709367/is-it-possible-to-use-labelfor-for-header-row-in-index-view#13305681) – StuartLC Nov 09 '12 at 09:58
14

This is the pattern I've followed. This assumes Lists are never null, but can be empty, but condition can easily be short circuited for a null list. I do like Brian's extension method though.

@if (Model.BurgersList.Count > 0)
{
  var meta = Model.BurgersList.First();
   <table>
        <tr>
            <th>
                @Html.DisplayNameFor(m => meta.Title)
            </th>
            <th>
                @Html.DisplayNameFor(m => meta.HasMustard)
            </th>
         //etc....
   @foreach (var item in Model.AssignmentDefinitions)
   {
        <tr>
            <td>
                @Html.DisplayFor(m => item.Title)
            </td>
            <td>
                @Html.DisplayFor(m => item.HasMustard)
            </td>
       //etc...
   }
   </table>
}
else
{ 
    @:No burgers available.  Create(usually make this an action link)  a new one.
}
AaronLS
  • 37,329
  • 20
  • 143
  • 202
4
//MVC4 has the DisplayNameFor Extensions

public static class HtmlHelperExtensions
{
    public static MvcHtmlString DisplayNameFor<TModel, TProperty>(this HtmlHelper<IEnumerable<TModel>> helper, Expression<Func<TModel, TProperty>> expression)
    {
        return DisplayNameFor(expression);
    }

    public static MvcHtmlString DisplayNameFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
    {
        return DisplayNameFor(expression);
    }

    private static MvcHtmlString DisplayNameFor<TModel, TProperty>(Expression<Func<TModel, TProperty>> expression)
    {
        var metadata = ModelMetadata.FromLambdaExpression<TModel, TProperty>(expression, new ViewDataDictionary<TModel>());
        var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
        string s = metadata.DisplayName ?? (metadata.PropertyName ?? htmlFieldName.Split(new char[] { '.' }).Last<string>());
        return new MvcHtmlString(HttpUtility.HtmlEncode(s));
    }
}
yongfa365
  • 352
  • 3
  • 7