0

I'm trying to access the properties of a class dynamically in ASP.NET Razor when generating an HTML Table. This problem is normally easily solved with reflection, but the @Html.DisplayFor method is giving me issues.

I am attempting to generate an HTML table that has 3 cells per row, with the title of the item in bold as the first line of the cell, and the value of the item in the second line of the cell. The contents of the table should not include cells which are on the 'Excluded Fields' list, and I do not want to have to statically reference each column.

<table class="blpSecurityTable">
                <tr>
                    @{
                        int _rowCount = 0;
                        foreach (var property in item.GetType().GetProperties())
                        {
                            @if (!Model.ExcludedFields.Contains(@property.Name))
                            {
                                dynamic test = @property.GetValue(item);
                                <td><b>@Html.DisplayFor(m => @property.Name)</b><br />@Html.DisplayFor(m => @test)</td> 
                                _rowCount++;
                            }
                            @if (_rowCount % numCols == 0)
                            {
                                @:</tr><tr>
                            }
                        }
                    }
                </tr>
            </table>

I've tried calling @Html.DisplayFor(m => @property.GetValue(item)) but that just creates a runtime error. I can simply call @property.GetValue(item) and the value displays, but this is not ideal because I use display templates to do things like set dates to the ShortDateString format.

I understand that DisplayFor is using reflection to determine the type of the property, and that is why I am trying to use the dynamic variable to facilitate reflection for the method. However, when I run the method, it throws errors indicating the variable is not a generic parameter, and therefore cannot share its attributes. The resulting page has mostly blank values, and some cells filled in with unexpected descriptive information.

I feel like I'm getting close, but I don't know how to proceed. The page won't look right if I don't pass the values into an HTML display method, and I cannot think of any other way to get the type of table I want to be generated. Thoughts?


The issue is solved by creating a Display Template for the Security object, which then allowed me to properly use the @Html.Display Method, because the Model for the Display Template has an entry for the property.

Here is what the page code looks like now:

Display Template

@model Interface.Models.Security
@{int numCols = 3;}
<table class="blpSecurityTable">
    <tr>
        @{
            int _rowCount = 0;
            foreach (var property in Model.GetType().GetProperties())
            {
                @if (!BLPDLModel.ExcludedFields.Contains(@property.Name))
                {
                    <td><b>@Html.DisplayFor(m => @property.Name)</b><br />@Html.Display(property.Name)</td>
                    _rowCount++;
                }
                @if (_rowCount % numCols == 0)
                {
                @:</tr><tr>
                }
            }
        }
    </tr>
</table>

Razor Page

@foreach (var item in Model.Security)
{
<div class="blpSecurityItem">
<button type="button" class="collapsible">{button text}</button>
    @{
        <div class="collapsible-content">
            <hr />
            @Html.DisplayFor(m => item)
        </div>
    }
</div>
  • remove @ from @property in @Html.DisplayFor(m => @property.GetValue(item)). You can print output in variable pass. – daremachine Dec 02 '19 at 15:52
  • Same error. `Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.` – Philippe Haussmann Dec 02 '19 at 16:00
  • @Html.DisplayFor is for model. You can not pass type from outside. DisplayFor need variable of model not value, it is not setter. Check this https://stackoverflow.com/questions/6365633/what-is-the-html-displayfor-syntax-for – daremachine Dec 02 '19 at 16:09
  • I see. So display for is reading the model not the value type. I think my best solution, in that case, may be to build a custom display template for the security model that meets my requirements. I'm trying to avoid having to update it every time a column is added to the table (as the table will expand), do you think that is possible? Or can I simply not loop over the values and use razor html classes. – Philippe Haussmann Dec 02 '19 at 16:31

1 Answers1

1

Replace @Html.DisplayFor(m => @test) with @Html.Display(property.Name)

and your model property for date should have [DisplayFormat(DataFormatString = "{0:yyyy/MM/dd}")] or anything you like

rjs123431
  • 688
  • 4
  • 14
  • This seems to simply generate a blank string. Besides, this would only get me the name of the property as specified in the PropertyInfo object generated by the reflection. I want the value of the property as it is stored in the object to be displayed and formatted correctly. – Philippe Haussmann Dec 02 '19 at 16:41
  • 1
    Did you try it? `@Html.Display` will display the value of the property. It is the same as `@Html.DisplayFor` but you pass the name of the property instead of a lambda expression – rjs123431 Dec 02 '19 at 16:43
  • Ah! My apologies, I misunderstood the syntax of the command. I did try it, and it returns only a blank string, but based on that description it should be returning what I want. Sorry for the confusion on my end. – Philippe Haussmann Dec 02 '19 at 16:49
  • This steered me in the correct direction! I was able to solve the problem by creating a display template for the Security object. At that level, I can reference the property name from the @Html.Display Function, and it works! I will add an answer with the updated code, but will leave yours as marked correct so you get credit for steering me in the right direction! :) – Philippe Haussmann Dec 02 '19 at 17:14