Model Property Display Name using Reflection
Asked Answered
O

4

1

I am working on a project where I need to output a few hundred properties on to the screen. To save myself lots of tedious markup, I decided to use reflection.

//markup removed to keep this concise
@for (var i = 0; i < Model.SiteAndJobDetails.GetType().GetProperties().Count(); i++)
{
  @Model.SiteAndJobDetails.GetType().GetProperties()[i].Name
  @Model.SiteAndJobDetails.GetType().GetProperties()[i].GetValue(Model.SiteAndJobDetails, null)
}

Although slower to render, this will save me writing out about 2 hundred properties and values with HTML helpers. At least, that was the plan. However, I need to use @Html.DisplayNameFor or something similar to pick up the Display attribute value from the property.

My intial thoughts were

@Html.DisplayNameFor(m=>@Model.SiteAndJobDetails.GetType().GetProperties()[i].Name)

But that does not work, I would imagine because I am using reflection here to get the property name. Is there another way?

Oxford answered 27/3, 2014 at 11:2 Comment(0)
O
2

@Andrei was correct to use the ViewData.ModelMetadata but had the syntax slightly off. The correct syntax is

@ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails")
.Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name)
.DisplayName

The final solution is to check the property exists, if it does use it, otherwise use the property name

@if (!string.IsNullOrEmpty(@ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails").Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name).DisplayName))
{
    @ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails").Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name).DisplayName
}
else
{
    @Model.SiteAndJobDetails.GetType().GetProperties()[i].Name
}
Oxford answered 27/3, 2014 at 12:44 Comment(0)
G
2

You can get it using the Metadata (that's what the framework does anyway):

string displayName = ViewData.ModelMetadata.Properties
      .Where(x => x.PropertyName == Model.SiteAndJobDetails.GetType()
                                   .GetProperties()[i].Name)
      .SingleOrDefault()
      .DisplayName;
Gavingavini answered 27/3, 2014 at 11:11 Comment(2)
Thanks for pointing me in the right direction. Your syntax was a little off so I will edit the answer for future searchers. Once the edit is accepted I will accept. Thanks very much for your helpOxford
@James, the edit was rejected. Consider adding your solution either as an answer or an edit in your original post.Gavingavini
O
2

@Andrei was correct to use the ViewData.ModelMetadata but had the syntax slightly off. The correct syntax is

@ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails")
.Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name)
.DisplayName

The final solution is to check the property exists, if it does use it, otherwise use the property name

@if (!string.IsNullOrEmpty(@ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails").Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name).DisplayName))
{
    @ViewData.ModelMetadata.Properties.First(x => x.PropertyName == "SiteAndJobDetails").Properties.SingleOrDefault(x => x.PropertyName == Model.SiteAndJobDetails.GetType().GetProperties()[i].Name).DisplayName
}
else
{
    @Model.SiteAndJobDetails.GetType().GetProperties()[i].Name
}
Oxford answered 27/3, 2014 at 12:44 Comment(0)
B
0

Alternatively, add this to your razor:

@functions {
string R<TCLASS>(Expression<Func<TCLASS, Object>> expression) //Lambda = x => x.TPROPERTY
{
    var memberExpression = expression.Body as MemberExpression;
    if(memberExpression == null)
    {
        memberExpression = (MemberExpression) ((UnaryExpression)expression.Body).Operand;
    }

    return memberExpression.Member.Name;
}
}

Then you can:

@(R<ModelClassName>(x => x.PropertyName)) //Outputs "PropertyName"

I like using this when writing JavaScript that receives JSON. It lets you do this:

<script>
    function recieveJsonResult(classNameDto) {
        var classProperty = classNameDto.@(R<ClassNameDto>(x => x.PropertyName));
    }
</script>

This way you get autocomplete inside the lambda and you are free to rename the property without worrying about breaking your front end code.

Badger answered 22/2, 2018 at 15:59 Comment(0)
M
0

I prefer this answer: https://mcmap.net/q/1723710/-how-to-show-displayname-and-value-of-each-property-in-model-dynamically

Updated to today's syntax:

var displayName = property.GetCustomAttribute<DisplayAttribute>().Name;

Or this one's even easier actually: https://mcmap.net/q/1502540/-loop-through-model-properties-in-reflection-then-use-html-helpers-to-display-how-to-get-concrete-property-back

@Html.Label(property.Name);
Meetinghouse answered 27/12, 2018 at 10:45 Comment(1)
This is certainly easy, except it doesn't work if your class has properties that are themselves classes. In that case you'll need to build the appropriate dot-notation for the property chain so that you have the correct name/id for the form control.Sunny

© 2022 - 2024 — McMap. All rights reserved.