Calling IEnumerable overload of DisplayNameFor [du

2019-01-23 03:01发布

问题:

This question already has an answer here:

  • DisplayNameFor() From List<Object> in Model 3 answers

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)

回答1:

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>
...


回答2:

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.