Reputation: 7821
This is for an ASP.NET MVC 4 application in a Razor view. I am passing a model to a partial view and am trying to iterate through a given list of properties contained in the model to be displayed as a table.
Given something like List<string> propertyNames
, in this table, I would like to output the DisplayNameFor
and the value of the property in a structure like so:
<tr>
<th>@Html.DisplayNameFor(Model.property)</th>
<td>@Model.property</td>
</tr>
I'll have to do this a few times in the partial because different properties correspond to different div
elements in the partial where different tables will be inserted so I've created a helper and this is where I get hung up. First, the only way I know how to do this is reflection, and I have read that reflection is expensive especially for just doing one property at a time. Also, using this method, I can't get @Html.DisplayNameFor
to work correctly because, using reflection, I don't quite know the syntax:
@helper IterateDetailPropertyNames(List<string> propertyNames) {
foreach (var property in propertyNames)
{
<tr>
<th>
@Html.DisplayNameFor(m => m.GetType().GetProperty(property).GetValue(Model, null));
@*Error: Templates can be used only with field access, property access...*@
</th>
<td>
@Model.GetType().GetProperty(property).GetValue(Model, null)
</td>
</tr>
}
}
How can I make this work and improve this?
Upvotes: 1
Views: 115
Reputation:
A models ModelMetadata
gives you the data you need to generate your table. It contains the model value in additional to properties such as the DisplayName
, DisplayFormatString
, NullDisplayText
etc that are determined from annotations applied to your properties.
For more information of ModelMetadata
, refer the documentation and ASP.NET MVC 2 Templates, Part 2: ModelMetadata.
To generate your table, you could write the following HtmlHelper
extension method
public static class TableHelper
{
public static MvcHtmlString DisplayTableFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
{
// Get the ModelMetadata for the model
ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
StringBuilder html = new StringBuilder();
// Loop through the ModelMetadata of each property in the model
foreach (ModelMetadata property in metaData.Properties)
{
var x = metaData.DisplayFormatString;
string label = property.DisplayName;
string value = null;
if (property.Model == null)
{
value = metaData.NullDisplayText;
}
else if (metaData.DisplayFormatString != null)
{
value = string.Format(metaData.DisplayFormatString, property.Model);
}
else
{
value = property.Model.ToString();
}
TagBuilder labelCell = new TagBuilder("td");
labelCell.InnerHtml = label;
TagBuilder valueCell = new TagBuilder("td");
valueCell.InnerHtml = value;
StringBuilder innerHtml = new StringBuilder();
innerHtml.Append(labelCell.ToString());
innerHtml.Append(valueCell.ToString());
TagBuilder row = new TagBuilder("tr");
row.InnerHtml = innerHtml.ToString();
html.Append(row.ToString());
}
TagBuilder table = new TagBuilder("table");
table.InnerHtml = html.ToString();
return new MvcHtmlString(table.ToString());
}
}
and in the view
@using YourAssembly.TableHelper
....
@Html.DisplayTableFor(m => m)
You can also include the assembly in your web.config
file so that the @using
statement is not required in the view
As s side note, <table>
elements are for tabular data and should not be used for layout. Instead consider using styled <div>
elements
Upvotes: 3