Best way to get table header names in MVC 4
asp.net-mvc, asp.net-mvc-4
Solution
The Problem
As Thomas pointed out, Chris's answer works in some cases, but runs into trouble when using a ViewModel because the nested properties don't enjoy the same automatic resolution. This works if your model type is `IEnumerable<Type>`, because the DisplayNameFor lambda can still access properties on the model itself:
However, if the `ClientDetail` collection is nested inside of a ViewModel, we can't get to the item properties from the collection itself:
The Solution
As pointed out in DisplayNameFor() From List in Model, your solution is actually perfectly fine. This won't cause any issues if the collection is null because the lambda passed into `DisplayNameFor` is never actually executed. It's only uses it as an expression tree to identify the type of object.
So any of the following will work just fine:
@Html.DisplayNameFor(model => model.Details[0].ClientNumber)
@Html.DisplayNameFor(dummy => Model.Details.FirstOrDefault().ClientNumber)
@{ ClientDetail dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.ClientNumber)
Further Explanation
If we want to see some of the fancy footwork involved in passing an expression, just look at the source code on DisplayNameFor or custom implementations like DescriptionFor. Here's an simplified example of what happens when we call DisplayNameFor with the following impossible expression:
@Html.DisplayNameFor3(model => model.Details[-5].ClientNumber)
Notice how we go from `model.Details.get_Item(-5).ClientNumber` in the lambda expression, to being able to identify just the member (`ClientNumber`) without executing the expression. From there, we just use reflection to find the `DisplayAttribute` and get its properties, in this case `Name`.
Problem
I'm trying to display a list of objects in a table. I can iterate over each individual item to find it's value (using an for loop or a DisplayTemplate), but how do I abitriarily pick one to display headers for the whole group. Here's an simplified example: Model: ``` public class ClientViewModel { public int Id { get; set; } public List<ClientDetail> Details { get; set; } } public class ClientDetail { [Display(Name="Client Number")] public int ClientNumber { get; set; } [Display(Name = "Client Forname")] public string Forname { get; set; } [Display(Name = "Client Surname")] public string Surname { get; set; } } ``` View ``` @model MyApp.ViewModel.ClientViewModel @{ var dummyDetail = Model.Details.FirstOrDefault(); } <table> <thead> <tr> <th>@Html.DisplayNameFor(model => dummyDetail.ClientNumber)</th> <th>@Html.DisplayNameFor(model => dummyDetail.Forname)</th> <th>@Html.DisplayNameFor(model => dummyDetail.Surname)</th> </tr> </thead> <tbody> @for (int i = 0; i < Model.Details.Count; i++) { <tr> <td>@Html.DisplayFor(model => model.Details[i].ClientNumber)</td> <td>@Html.DisplayFor(model => model.Details[i].Forname)</td> <td>@Html.DisplayFor(model => model.Details[i].Surname)</td> </tr> } </tbody> </table> ``` Notice: I'm using `var dummyDetail = Model.Details.FirstOrDefault();` to get a single item whose properties I can access in `DisplayNameFor`. - What would be the best way to access those headers ? - Will this break if the collection is null? - Should I just replace them with hard coded plain text labels?