ASP.net MVC - 显示集合的模板

时间:2011-11-03 21:23:28

标签: asp.net-mvc asp.net-mvc-3 display-templates

我在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 个答案:

答案 0 :(得分:61)

像这样:

@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>

答案 1 :(得分: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”。

我希望这有一些价值。

答案 2 :(得分:7)

在我关于不从视图中获取输出的问题中,我实际上有一个示例,说明如何使用子模型集合模拟模型并将它们全部渲染。

ASP.NET Display Templates - No output

基本上,您需要创建一个子类List<T>Collection<T>的模型并使用它:

@model ChildModelCollection 

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

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

所以对于OP问题:

public class ChildModelCollection : Collection<ChildModel> { }

将构建一个强类型模型,该模型可以像其他模板一样被解析为模板。

答案 3 :(得分:1)

实际的“有效答案”是-IMHO-没有正确回答问题。我认为OP正在寻找一种方法来获得一个列表模板,该模板在不指定UIHint的情况下触发。

魔法的东西几乎完成了这项工作

某些魔法加载了指定类型的正确视图 对于 指定类型的集合,更多魔法加载相同的视图
应该是为某个指定类型的集合迭代相同视图的魔法。

更改实际行为?

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

使用实际的魔法

我想出了一些有用的东西。创建显示模板~/Views/Shared/DisplayTemplates/MyModel.cshtml

将模型声明为类型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