@Html.DisplayNameFor for details model

2019-03-17 20:19发布

问题:

In my model I have an Entity

public class Carrier
{
public Guid CarrierId { get; set; }
public string Name { get; set; }
}

I also have a ViewModel

public class CarrierIndexViewModel
{
public IEnumerable<Carrier> Carriers { get; set; }
public PagingInfo PagingInfo { get; set; }
}

I have a strongly-typed (CarrierIndexViewModel) Index View, which is suppose to display a table of Carrier using PagingInfo.

I'm trying to use Html helper to display Carrier.Name in my table's header When I used IEnumerable as a model for this view I had @Html.DisplayFor(model => model.Name)

How can I get same result using my new CarrierIndexViewModel to display title for Carrier.Name?

回答1:

You can add a @using statement to your View to specify the Carrier type, then you will need to loop through your Model's Carriers property.

Your View will look something like..

@model CarrierIndexViewModel
@using Carrier

@foreach(Carrier c in Model.Carriers)
{
 @Html.DisplayFor(model => c.Name)
}


回答2:

I found there was no need for helper methods, extra code or looping through the collection. I just used the following:

@Html.DisplayNameFor(model => model.Carriers.FirstOrDefault().Name)

This still works even if FirstOrDefault() would return null because it's only looking for meta data, the Name property itself is not accessed.

Many thanks to @Kurian who inspired this answer.



回答3:

I've got a similar problem, because i don't want to type my column names by hand so i wrote a helper:

public static IHtmlString DisplayColumnNameFor<TModel, TClass, TProperty>
    (this HtmlHelper<TModel> helper, IEnumerable<TClass> model,
    Expression<Func<TClass, TProperty>> expression)
{ 
    var name = ExpressionHelper.GetExpressionText(expression);
    name = helper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
    var metadata = ModelMetadataProviders.Current.GetMetadataForProperty(
        () => Activator.CreateInstance<TClass>(), typeof(TClass), name);

    return new MvcHtmlString(metadata.DisplayName);
}

Then in the view you'll call it

@Html.DisplayColumnNameFor(Model.Carriers, m => m.Name)

Please note that you can use Display attribute which accepts Name and many other customization like Resource file etc.

public class Carrier
{
  [Display(Name="Carrier ID")]
  public Guid CarrierId { get; set; }
  [Display(Name="Carrier Name")]
  public string Name { get; set; }
}


回答4:

Here is an approach, which is based on the original DisplayNameFor extension method and works consistent.

An extension method for HtmlHelper:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;

public static class HtmlHelperExtensions
{
    public static MvcHtmlString DisplayNameFor<TModel, TEnumerable, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, IEnumerable<TEnumerable>>> enumerableExpression, Expression<Func<TEnumerable, TValue>> valueExpression)
    {
        var metadata = ModelMetadata.FromLambdaExpression(valueExpression, new ViewDataDictionary<TEnumerable>());
        string displayName = metadata.DisplayName ?? metadata.PropertyName ?? ExpressionHelper.GetExpressionText(valueExpression).Split('.').Last();
        return new MvcHtmlString(HttpUtility.HtmlEncode(displayName));
    }
}

And that's how it could be used in a view:

@Html.DisplayNameFor(m => m.Carriers, c => c.Name)


回答5:

I had a similar requirement, but I needed to get display names for properties from a different non-enumerable class so the solution from @plurby wouldn't have worked. I ended up solving this by creating a fluent call that can be used to get a new HtmlHelper for any arbitrary type. The code for the extension and adapter is:

public static class HtmlHelpers
{
    public static HtmlHelperAdapter<TModel> Adapt<TModel>(this HtmlHelper<TModel> helper)
    {
        return new HtmlHelperAdapter<TModel>(helper);
    }

    public class HtmlHelperAdapter<TModel>
    {
        private readonly HtmlHelper<TModel> _helper;

        public HtmlHelperAdapter(HtmlHelper<TModel> helper)
        {
            _helper = helper;
        }

        public HtmlHelper<TNewModel> For<TNewModel>()
        {
            return new HtmlHelper<TNewModel>(_helper.ViewContext, new ViewPage(), _helper.RouteCollection);
        }
    }
}

You can then use this in the view like this:

<td>@(Html.Adapt().For<MyOtherModel>().DisplayNameFor(m => m.SomeProperty))</td>

One of the advantages is that this approach will work for any HtmlHelper extension, not just DisplayNameFor (but keep in mind that the adapted HtmlHelper does not have any view data since that's all typed based on the model being used in the view and thus won't work in the adapted HtmlHelper).