24

This works for grabbing the headers(NOT VALUES):

@model IEnumerable<SomeModel>
...
<th>@Html.DisplayNameFor(m => m.SomeModelProperty)</th>

Which if SomeModelProperty were:

[Display(Name = "An Excellent Header")]
SomeModelProperty { get; set; }

Then it would display "An Excellent Header" in the header th element.

You would think this wouldn't work because the model is IEnumerable, which wouldn't have a m.SomeModelProperty, but it works because HtmlHelper has a HtmlHelper<IEnumerable<TModel>> such that the parameter of the lambda is TModel, not IEnumerable<TModel>. Since this just uses metadata, there is no need for an item from the collection. (Although intellisense on m. will lie to you and make you think it's a collection). I'm not sure when this cool overload was added, but is quite handy for Index.cshtml and does away with funky things like @Html.DisplayNameFor(m => @Model.FirstOrDefault().SomeModelProperty) which I want to avoid.

http://msdn.microsoft.com/en-us/library/hh833697(v=vs.108).aspx

However, I can't figure out how to get this to work when my model is not IEnumerable, but instead contains IEnumerable as a property, such as:

public class SomeList
{
   public List<SomeModel> SomeModels { get; set; }
   public int Page { get; set; }
   public DateTime CurrentAsOf { get; set; }
}

I was hoping to be explicit with the generic type parameters, but I think the type parameters are specified by the engine that trickles down from the HtmlHelper created with the page. Can I declare a new HtmlHelper in the page, or somehow specify the type parameters explicitly?

Index.cshtml:

@model SomeList
//No idea how to do this:
@Html.DisplayNameFor<IEnumerable<SomeModel>>(m => m.SomeModelProperty)
AaronLS
  • 37,329
  • 20
  • 143
  • 202
  • 2
    In your loop code in your Razor file, you could check if it is the **first** element in the loop, and output the `` section using the `DisplayNameFor` calls. This `if` would be before the part where you output the data in a `tr`. – Gromer Oct 17 '12 at 19:07
  • 1
    Yes I've used these types of workarounds am hoping to move away from that now that DisplayNameFor supports getting metadata from IEnumerable – AaronLS Oct 17 '12 at 19:27
  • This question has a good and working solution (accepted answer): http://stackoverflow.com/questions/20807869/displaynamefor-from-listobject-in-model – lxa Feb 09 '15 at 17:21
  • @Ixa Technically that question is a duplicate of this question since mine came first, but oh well. Nice find :) Voted to close mine. – AaronLS Feb 09 '15 at 18:10
  • 1
    Note that if you some property is IEnumerable, rather than IList, you can access it via @Html.DisplayNameFor(m => m.SomeModels.First().Item) NB: First() is not actually run, so does not throw an exception if SomeModels is empty – Peter Kerr Jun 05 '15 at 13:42

2 Answers2

44

Another similar workaround that works even if there are no rows could be:

...
@{var dummy = Model.FirstOrDefault(); }
    <tr>
        <th>
            @Html.DisplayNameFor(model => dummy.SomeModelProperty)
        </th>
...
franz
  • 457
  • 3
  • 4
  • 4
    The call is ambiguous between the following methods or properties: 'System.Web.Mvc.Html.DisplayNameExtensions.DisplayNameFor(System.Web.Mvc.HtmlHelper>, System.Linq.Expressions.Expression>)' and 'System.Web.Mvc.Html.DisplayNameExtensions.DisplayNameFor,int>... – Oleg Sh Jan 17 '14 at 18:19
  • 1
    same problem as Oleg Sh... – sensei Jun 21 '14 at 16:54
  • 1
    Did anyone manage to solve this problem? – user3407039 Feb 06 '15 at 14:05
6

I have exactly the same issue because I am using ViewModels so I have a ViewModel with an IEnumerable of actual objects as a property.

I did come across this post where if you check the answer the guy has created his own HTMLHelper for it to solve this issue http://forums.asp.net/t/1783733.aspx. His version is:

public static MvcHtmlString DisplayColumnNameFor<TModel, TClass, TProperty>(
    this HtmlHelper<TModel> helper, IEnumerable<TClass> model, 
    Expression<Func<TClass, TProperty>> expression)
{
    var name = ExpressionHelper.GetExpressionText(expression);
    name = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
    var metadata = ModelMetadataProviders.Current.GetMetadataForProperty(
        () => Activator.CreateInstance<TClass>(), typeof(TClass), name);

    return new MvcHtmlString(metadata.DisplayName);
}

You have to pass two arguments enumeration and expression rather than the normal just expression so you may prefer @franz answer. I can't see there being anyway round having to pass 2 arguments since it needs to know which property of the view model you are applying the expression to.

gunr2171
  • 16,104
  • 25
  • 61
  • 88
Alan Macdonald
  • 1,872
  • 20
  • 36