ASP.net MVC - шаблон отображения коллекции
У меня есть следующая модель в 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)
, не жалуясь на неправильный тип модели?
Ответы
Ответ 1
Вот так:
@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>
Ответ 2
Это ответ на комментарий Маслоу. Это мой первый вклад в SO, поэтому у меня недостаточно репутации, чтобы комментировать - отсюда ответ в качестве ответа.
Вы можете установить свойство "TemplateHint" в ModelMetadataProvider. Это автоматически подключит любой 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;
}
Вы в основном переопределяете метод CreateMetadataFromPrototype для 'CachedDataAnnotationsModelMetadataProvider' и регистрируете свой производный тип как предпочтительный 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" на моделях.
Я надеюсь, что это имело какое-то значение.
Ответ 3
В моем вопросе о том, чтобы не получать вывод из представлений, на самом деле у меня есть пример того, как шаблонировать модель с коллекцией дочерних моделей и иметь их все рендеринг.
Шаблоны отображения ASP.NET - нет вывода
По существу, вам нужно создать модель, которая подклассы List<T>
или Collection<T>
и использовать это:
@model ChildModelCollection
@foreach (var child in Model)
{
Html.DisplayFor(m => child);
}
В вашем шаблоне для модели коллекции итерации и рендеринга детей. Каждому ребенку требуется строго типизировать, поэтому вы можете также создать свои собственные типы моделей для этих элементов и иметь шаблоны для них.
Итак, для вопроса OP:
public class ChildModelCollection : Collection<ChildModel> { }
Создает сильно типизированную модель, которая может быть решена для шаблона, как и любой другой.
Ответ 4
Фактический "действительный ответ" есть -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
.