模型中的 List < Object > 中的 DisplayNameFor()

我相信这是非常简单的,我只是似乎找不到正确的方式来显示一个项目在我的模型中的列表名称。

我的简化模型:

public class PersonViewModel
{
public long ID { get; set; }
    

private List<PersonNameViewModel> names = new List<PersonNameViewModel>();


[Display(Name = "Names")]
public List<PersonNameViewModel> Names { get { return names; } set { names = value; } }
}

姓名:

public class PersonNameViewModel
{
public long ID { get; set; }


[Display(Name = "Set Primary")]
public bool IsPrimary { get; set; }


[Display(Name = "Full Name")]
public string FullName { get; set; }
}

现在我想制作一个表格来显示一个人的所有名字,并得到 DisplayNameFor FullName。显然,

@Html.DisplayNameFor(model => model.Names.FullName);

不起作用

@Html.DisplayNameFor(model => model.Names[0].FullName);

是否有「最佳方法」取得显示名称?

37494 次浏览

This actually works, even without items in the list:

@Html.DisplayNameFor(model => model.Names[0].FullName)

It works because MVC parses the expression instead of actually executing it. This lets it find that right property and attribute without needing there to be an element in the list.

It's worth noting that the parameter (model above) doesn't even need to be used. This works, too:

@Html.DisplayNameFor(dummy => Model.Names[0].FullName)

As does this:

@{ Namespace.Of.PersonNameViewModel dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.FullName)

There is another way for do it, and i guess that is more clear:

public class Model
{
[Display(Name = "Some Name for A")]
public int PropA { get; set; }


[Display(Name = "Some Name for B")]
public string PropB { get; set; }
}


public class ModelCollection
{
public List<Model> Models { get; set; }


public Model Default
{
get { return new Model(); }
}
}

And then, in the view:

@model ModelCollection


<div class="list">
@foreach (var m in Model.Models)
{
<div class="item">
@Html.DisplayNameFor(model => model.Default.PropA): @m.PropA
<br />
@Html.DisplayNameFor(model => model.Default.PropB): @m.PropB
</div>
}
</div>

I like T-moty's solution. I needed a solution using generics so my solution is essentially this:

public class CustomList<T> : List<T> where T : new()
{
public static async Task<CustomList<T>> CreateAsync(IQueryable<T> source)
{
return new CustomList<T>(List<T> list); //do whatever you need in the contructor, constructor omitted
}


private T defaultInstance = new T();


public T Default
{
get { return defaultInstance; }
}
}

Using this in the view is the same as his example. I create a single instance of an empty object so I'm not creating a new instance every time I reference Default.

Note, the new() constraint is needed in order to call new T(). If your model class doesn't have a default contructor, or you need to add arguments to the constructor you can use this:

private T defaultInstance = (T)Activator.CreateInstance(typeof(T), new object[] { "args" });

The view will have an @model line like:

@model CustomList<Namespace.Of.PersonNameViewModel.Model>

As an alternate solution you could try:

@Html.DisplayNameFor(x => x.GetEnumerator().Current.ItemName)

It will work even if the list is empty!

In ASP.NET Core, Html.DisplayNameForInnerType() solves this. It can be used this way:

Html.DisplayNameForInnerType((PersonNameViewModel person) => person.FullName)

Note that the type PersonNameViewModel has to be explicitly specified in the lambda expression's parameter.

This is what the API documentation has to say:

Returns the display name for the specified expression if the current model represents a collection.

It works when the current Model itself is a collection and also when one of its members is a collection.