1

I'm having trouble figuring out a clean way to do this. I have a ViewModel class that contains a collection of rows for a table. I would like to be able to do an @Html.DisplayNameFor() on the type in the strongly-typed collection without referring to the first item in the collection or creating a new instance of the type. So to clarify here's an example:

public class TableViewModel
{
  public string Title { get; set; }
  public IEnumerable<Row> Rows { get; set; }
}

public class Row
{
  public int ColumnA { get; set; }
  public int ColumnB { get; set; }
}

//In the Razor view
<table>
  <tr>
    <th>@Html.DisplayNameFor(???)</th>
  </tr>
</table>

Is there a way to do this without doing @Html.DisplayNameFor(x => x.Rows.First().ColumnA)?

Brian
  • 5,069
  • 7
  • 37
  • 47
Eric Andres
  • 3,417
  • 2
  • 24
  • 40
  • Is this what you are trying to do? http://stackoverflow.com/questions/3885796/get-displayname-attribute-without-using-labelfor-helper-in-asp-net-mvc – Levi Botelho Jan 28 '13 at 20:54
  • 1
    You could add `public Row Row { get; set; }` to your view model. – Forty-Two Jan 28 '13 at 20:58
  • @LeviBotelho - I saw that question in my research, but it doesn't exactly fit the bill, since it doesn't deal with a collection. – Eric Andres Jan 28 '13 at 21:03
  • @Forty-Two - I guess what I'm envisioning is a helper method solution. I'm not crazy about adding an extra property to my VM. – Eric Andres Jan 28 '13 at 21:07

2 Answers2

1

You could use the this.ViewData.ModelMetadata.Properties collection and call the GetDisplayName method to generate the header cell content. We do this generically in our code to render an arbitrary model as a table.

Edit: To expand on this, I use a basic model that represents a table and only a table. I have 3 classes defined. I have a TableRowCell' class, aTableRow' class (which is essentially a collection of TableRowCell objects) and a Table class.

public class Table
{
    public Table(String name)
    {
        this.Name = name;
        this.ContentRows = new List<TableRow>();
        this.HeaderRow = new TableRow();
        this.FooterRow = new TableRow();
    } 

    public IList<TableRow> ContentRows
    {
        get;
        private set;
    }

    public TableRow FooterRow
    {
        get;
        private set;
    }

    public TableRow HeaderRow
    {
        get;
        private set;
    }

    public String Name
    {
        get;
        private set;
    }
}

When I have a view model that contains a collection of objects that I want to display in a table I call first an HtmlHelper extension that converts that collection into a Table object. Inside that method I iterate over a call to ModelMetadataProviders.Current.GetMetadataForType(null, typeof(TModel)).Properties..Where(item => item.ShowForDisplay) to get a collection of metadata objects used to generate the header cells. I then iterate over the collection of items and call ModelMetadataProviders.Current.GetMetadataForType(() => item, typeof(TModel)).Properties..Where(item => item.ShowForDisplay) to get a collection of metadata objects used to generate the content cells.

Technically, my HtmlHelper extension method actually returns a TableBuilder object which will has a method called Render on it which generates the html. That setup has served me well so far.

Sacrilege
  • 795
  • 9
  • 25
  • How would you do this for the type generic type in an IEnumerable? I.E for IEnumerable, how do I get the metadata for Row? – Eric Andres Jan 29 '13 at 17:54
  • I elaborated in my answer a bit in an attempt to address your question. – Sacrilege Jan 29 '13 at 22:48
  • Excellent, the `ModelMetadataProvers.Current.GetMetadataForType` method was just what I was looking for. Thanks for sharing your solution. – Eric Andres Jan 30 '13 at 15:49
0
public class TableViewModel
{
  public string Title { get; set; }
  public IEnumerable<Row> Rows { get; set; }
  public Row ForNames = null;
}


@Html.DisplayNameFor(x => x.ForName.ColumnA)
James Curran
  • 101,701
  • 37
  • 181
  • 258