Подтвердить что ты не робот

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), не жалуясь на неправильный тип модели?

4b9b3361

Ответ 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.