我在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)
?
答案 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
。