46

我在 MVC 中有以下模型:

public class ParentModel
{
    public string Property1 { get; set; }
    public string Property2 { get; set; }

    public IEnumerable<ChildModel> Children { get; set; }
}

当我想显示父模型的所有子模型时,我可以这样做:

@Html.DisplayFor(m => m.Children)

然后我可以创建一个 ChildModel.cshtml 显示模板,DisplayFor 将自动遍历列表。

如果我想为 IEnumerable 创建自定义模板怎么办?

@model IEnumerable<ChildModel>

<table>
    <tr>
        <th>Property 1</th>
        <th>Property 2</th>
    </tr>
    ...
</table>

如何创建具有模型类型的显示模板,IEnumerable<ChildModel>然后调用@Html.DisplayFor(m => m.Children)而不抱怨模型类型错误?

4

4 回答 4

67

像这样:

@Html.DisplayFor(m => m.Children, "YourTemplateName")

或像这样:

[UIHint("YourTemplateName")]
public IEnumerable<ChildModel> Children { get; set; }

显然你会有~/Views/Shared/DisplayTemplates/YourTemplateName.cshtml

@model IEnumerable<ChildModel>

<table>
    <tr>
        <th>Property 1</th>
        <th>Property 2</th>
    </tr>
    ...
</table>
于 2011-11-03T21:31:27.423 回答
8

这是对马斯洛评论的回应。这是我对 SO 的第一次贡献,因此我没有足够的声誉来发表评论 - 因此将回复作为答案。

您可以在 ModelMetadataProvider 中设置“TemplateHint”属性。这会将任何 IEnumerable 自动连接到您指定的模板。我刚刚在我的项目中尝试过。下面的代码 -

protected override CachedDataAnnotationsModelMetadata CreateMetadataFromPrototype(CachedDataAnnotationsModelMetadata prototype, Func<object> modelAccessor)
    {
        var metaData = base.CreateMetadataFromPrototype(prototype, modelAccessor);
        var type = metaData.ModelType;

        if (type.IsEnum)
        {
            metaData.TemplateHint = "Enum";
        }
        else if (type.IsAssignableFrom(typeof(IEnumerable<object>)))
        {
            metaData.TemplateHint = "Collection";
        }

        return metaData;
    }

您基本上覆盖了“CachedDataAnnotationsModelMetadataProvider”的“CreateMetadataFromPrototype”方法,并将您的派生类型注册为首选的 ModelMetadataProvider。

在您的模板中,您不能直接访问集合中元素的 ModelMetadata。我使用以下代码访问集合中元素的 ModelMetadata -

@model IEnumerable<object>
@{ 
var modelType = Model.GetType().GenericTypeArguments[0];
var modelMetaData = ModelMetadataProviders.Current.GetMetadataForType(null, modelType.UnderlyingSystemType);

var propertiesToShow = modelMetaData.Properties.Where(p => p.ShowForDisplay);
var propertiesOfModel = modelType.GetProperties();

var tableData = propertiesOfModel.Zip(propertiesToShow, (columnName, columnValue) => new { columnName.Name, columnValue.PropertyName });
}

在我看来,我只需调用 @Html.DisplayForModel() 并加载模板。无需在模型上指定“UIHint”。

我希望这有一些价值。

于 2013-08-03T18:05:12.173 回答
7

在我关于不从视图中获取输出的问题中,我实际上有一个示例,说明如何使用一组子模型对模型进行模板化并让它们全部呈现。

ASP.NET 显示模板 - 无输出

本质上,您需要创建一个模型来继承List<T>Collection<T>使用它:

@model ChildModelCollection 

@foreach (var child in Model)
{
    Html.DisplayFor(m => child);
}

在集合模型的模板中迭代和渲染子级。每个孩子都需要强类型,因此您可能还想为这些项目创建自己的模型类型,并为这些项目创建模板。

所以对于OP问题:

public class ChildModelCollection : Collection<ChildModel> { }

将创建一个强类型模型,它是一个可以像其他任何模板一样解析为模板的集合。

于 2014-04-24T20:06:33.420 回答
3

实际的“有效答案”是-恕我直言-没有正确回答问题。我认为 OP 正在寻找一种无需指定 UIHint 即可触发的列表模板的方法。

神奇的东西几乎可以完成这项工作

一些魔法会加载指定类型的正确视图。
更多的魔法为指定类型的集合加载相同的视图应该 有一些魔法可以为指定类型的集合迭代相同的视图。

改变实际行为?

打开你最喜欢的反汇编程序。魔法发生在System.Web.Mvc.Html.TemplateHelpers.ExecuteTemplate. 如您所见,没有可扩展点来更改行为。也许对 MVC 的拉取请求可以帮助...

用真正的魔法去

我想出了一些可行的方法。创建一个显示模板~/Views/Shared/DisplayTemplates/MyModel.cshtml

将模型声明为 type object

如果对象是一个集合,则迭代并再次渲染模板。如果它不是一个集合,则显示该对象。

@model object

@if (Model is IList<MyModel>)
{
    var models = (IList<MyModel>)Model;
<ul>
    @foreach (var item in models)
    {
@Html.Partial("DisplayTemplates/MyModel", item)
    }
</ul>
} else {
    var item = (MyModel)Model;
    <li>@item.Name</li>
    }
}

现在DisplayFor没有UIHint.

于 2014-12-13T23:29:09.737 回答