在MVC 4中获取表头名称的最佳方法

时间:2022-09-15 20:08:01

I'm trying to display a list of objects in a table. I can iterate over each individual item to find it's value (using an for loop or a DisplayTemplate), but how do I abitriarily pick one to display headers for the whole group.

我正在尝试在表格中显示对象列表。我可以迭代每个单独的项目来查找它的值(使用for循环或DisplayTemplate),但是我如何非常选择一个来显示整个组的标题。

Here's an simplified example:

这是一个简化的例子:

Model:

public class ClientViewModel
{
    public int Id { get; set; }
    public List<ClientDetail> Details { get; set; }
}
public class ClientDetail
{
    [Display(Name="Client Number")]
    public int ClientNumber { get; set; }
    [Display(Name = "Client Forname")]
    public string Forname { get; set; }
    [Display(Name = "Client Surname")]
    public string Surname { get; set; }
}

View

@model MyApp.ViewModel.ClientViewModel

@{ var dummyDetail = Model.Details.FirstOrDefault(); }

<table>
    <thead>
        <tr>
            <th>@Html.DisplayNameFor(model => dummyDetail.ClientNumber)</th>
            <th>@Html.DisplayNameFor(model => dummyDetail.Forname)</th>
            <th>@Html.DisplayNameFor(model => dummyDetail.Surname)</th>
        </tr>
    </thead>
    <tbody>
        @for (int i = 0; i < Model.Details.Count; i++)
        {
              <tr>
                <td>@Html.DisplayFor(model => model.Details[i].ClientNumber)</td>
                <td>@Html.DisplayFor(model => model.Details[i].Forname)</td>
                <td>@Html.DisplayFor(model => model.Details[i].Surname)</td>
            </tr>
        }
    </tbody>
</table>

Notice: I'm using var dummyDetail = Model.Details.FirstOrDefault(); to get a single item whose properties I can access in DisplayNameFor.

注意:我正在使用var dummyDetail = Model.Details.FirstOrDefault();获取我可以在DisplayNameFor中访问其属性的单个项目。

  • What would be the best way to access those headers ?
  • 访问这些标题的最佳方法是什么?

  • Will this break if the collection is null?
  • 如果集合为空,这会破坏吗?

  • Should I just replace them with hard coded plain text labels?
  • 我应该用硬编码的纯文本标签替换它们吗?

2 个解决方案

#1


4  

The Problem

As Thomas pointed out, Chris's answer works in some cases, but runs into trouble when using a ViewModel because the nested properties don't enjoy the same automatic resolution. This works if your model type is IEnumerable<Type>, because the DisplayNameFor lambda can still access properties on the model itself:

正如托马斯指出的那样,克里斯的答案在某些情况下有效,但在使用ViewModel时会遇到麻烦,因为嵌套属性不能享受相同的自动分辨率。如果您的模型类型是IEnumerable ,则此方法有效,因为DisplayNameFor lambda仍然可以访问模型本身的属性:

在MVC 4中获取表头名称的最佳方法

However, if the ClientDetail collection is nested inside of a ViewModel, we can't get to the item properties from the collection itself:

但是,如果ClientDetail集合嵌套在ViewModel中,我们无法从集合本身获取项属性:

在MVC 4中获取表头名称的最佳方法

The Solution

As pointed out in DisplayNameFor() From List in Model, your solution is actually perfectly fine. This won't cause any issues if the collection is null because the lambda passed into DisplayNameFor is never actually executed. It's only uses it as an expression tree to identify the type of object.

正如DisplayNameFor()从模型中的列表中所指出的,您的解决方案实际上非常好。如果集合为null,这不会导致任何问题,因为传递给DisplayNameFor的lambda永远不会被实际执行。它仅将其用作表达式树来标识对象的类型。

So any of the following will work just fine:

所以以下任何一个都可以正常工作:

@Html.DisplayNameFor(model => model.Details[0].ClientNumber)

@Html.DisplayNameFor(dummy => Model.Details.FirstOrDefault().ClientNumber)

@{ ClientDetail dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.ClientNumber)

Further Explanation

If we want to see some of the fancy footwork involved in passing an expression, just look at the source code on DisplayNameFor or custom implementations like DescriptionFor. Here's an simplified example of what happens when we call DisplayNameFor with the following impossible expression:

如果我们想要看到传递表达式所涉及的一些花哨的步法,只需查看DisplayNameFor上的源代码或DescriptionFor之类的自定义实现。这是一个简单的例子,说明当我们使用以下不可能的表达式调用DisplayNameFor时会发生什么:

 @Html.DisplayNameFor3(model => model.Details[-5].ClientNumber)

在MVC 4中获取表头名称的最佳方法

Notice how we go from model.Details.get_Item(-5).ClientNumber in the lambda expression, to being able to identify just the member (ClientNumber) without executing the expression. From there, we just use reflection to find the DisplayAttribute and get its properties, in this case Name.

注意我们如何从lambda表达式中的model.Details.get_Item(-5).ClientNumber转到只能识别成员(ClientNumber)而不执行表达式。从那里,我们只使用反射来查找DisplayAttribute并获取其属性,在本例中为Name。

#2


1  

Sorry, your question is a little hard to understand, but I think the gist is that you want to get the display names for your properties, to use as headers, without requiring or first having to pick a particular item out of the list.

对不起,您的问题有点难以理解,但我认为要点是您希望获取属性的显示名称,用作标题,而不需要或首先必须从列表中选择特定项目。

There's already built-in support for this. You simply just use the model itself:

已经内置了对此的支持。您只需使用模型本身:

@Html.DisplayNameFor(m => m.ClientNumber)

In other words, just don't use a particular instance. DisplayNameFor has logic to inspect the class the list is based on to get the properties.

换句话说,就是不要使用特定的实例。 DisplayNameFor具有检查列表所基于的类以获取属性的逻辑。

#1


4  

The Problem

As Thomas pointed out, Chris's answer works in some cases, but runs into trouble when using a ViewModel because the nested properties don't enjoy the same automatic resolution. This works if your model type is IEnumerable<Type>, because the DisplayNameFor lambda can still access properties on the model itself:

正如托马斯指出的那样,克里斯的答案在某些情况下有效,但在使用ViewModel时会遇到麻烦,因为嵌套属性不能享受相同的自动分辨率。如果您的模型类型是IEnumerable ,则此方法有效,因为DisplayNameFor lambda仍然可以访问模型本身的属性:

在MVC 4中获取表头名称的最佳方法

However, if the ClientDetail collection is nested inside of a ViewModel, we can't get to the item properties from the collection itself:

但是,如果ClientDetail集合嵌套在ViewModel中,我们无法从集合本身获取项属性:

在MVC 4中获取表头名称的最佳方法

The Solution

As pointed out in DisplayNameFor() From List in Model, your solution is actually perfectly fine. This won't cause any issues if the collection is null because the lambda passed into DisplayNameFor is never actually executed. It's only uses it as an expression tree to identify the type of object.

正如DisplayNameFor()从模型中的列表中所指出的,您的解决方案实际上非常好。如果集合为null,这不会导致任何问题,因为传递给DisplayNameFor的lambda永远不会被实际执行。它仅将其用作表达式树来标识对象的类型。

So any of the following will work just fine:

所以以下任何一个都可以正常工作:

@Html.DisplayNameFor(model => model.Details[0].ClientNumber)

@Html.DisplayNameFor(dummy => Model.Details.FirstOrDefault().ClientNumber)

@{ ClientDetail dummyModel = null; }
@Html.DisplayNameFor(dummyParam => dummyModel.ClientNumber)

Further Explanation

If we want to see some of the fancy footwork involved in passing an expression, just look at the source code on DisplayNameFor or custom implementations like DescriptionFor. Here's an simplified example of what happens when we call DisplayNameFor with the following impossible expression:

如果我们想要看到传递表达式所涉及的一些花哨的步法,只需查看DisplayNameFor上的源代码或DescriptionFor之类的自定义实现。这是一个简单的例子,说明当我们使用以下不可能的表达式调用DisplayNameFor时会发生什么:

 @Html.DisplayNameFor3(model => model.Details[-5].ClientNumber)

在MVC 4中获取表头名称的最佳方法

Notice how we go from model.Details.get_Item(-5).ClientNumber in the lambda expression, to being able to identify just the member (ClientNumber) without executing the expression. From there, we just use reflection to find the DisplayAttribute and get its properties, in this case Name.

注意我们如何从lambda表达式中的model.Details.get_Item(-5).ClientNumber转到只能识别成员(ClientNumber)而不执行表达式。从那里,我们只使用反射来查找DisplayAttribute并获取其属性,在本例中为Name。

#2


1  

Sorry, your question is a little hard to understand, but I think the gist is that you want to get the display names for your properties, to use as headers, without requiring or first having to pick a particular item out of the list.

对不起,您的问题有点难以理解,但我认为要点是您希望获取属性的显示名称,用作标题,而不需要或首先必须从列表中选择特定项目。

There's already built-in support for this. You simply just use the model itself:

已经内置了对此的支持。您只需使用模型本身:

@Html.DisplayNameFor(m => m.ClientNumber)

In other words, just don't use a particular instance. DisplayNameFor has logic to inspect the class the list is based on to get the properties.

换句话说,就是不要使用特定的实例。 DisplayNameFor具有检查列表所基于的类以获取属性的逻辑。