Как создать EditorTemplate с помощью UIHint со свойством типа IEnumerable‹T›

В MVC вы можете создать шаблон редактора для T, а затем, когда вы хотите отобразить редактор для свойства типа IEnumerable<T>, вы можете просто сделать, например.

Html.EditorFor(m => m.MyListOfT)

Прелесть этого в том, что имена автоматически создаются фреймворком для входных данных, а затем при обратной отправке привязки модели все работает хорошо.

Мой вопрос: как вы делаете это, когда у вас есть несколько типов шаблонов редактора?

Я пытался использовать UIHint(), однако, похоже, он позволяет вам указать UIHint только для списка, а не для каждого элемента в списке. Это означает, что затем вам нужно создать EditorTemplate для списка с циклом foreach(), и тогда вы упустите хорошее автоматическое именование и привязку модели.

Что мне здесь не хватает?

Модель, например.

public class MyViewModel
{
    public IEnumerable<SomeType> SomeProperty { get; set; }
}

В идеале я хочу сделать что-то вроде:

public class MyViewModel
{
    [UIHint("SomeTypeTemplate")]
    public IEnumerable<SomeType> SomeProperty { get; set; }
}

и пусть это автоматически применяется ко всем элементам в списке, поэтому я могу отображать только с помощью:

Html.EditorFor(m => m.SomeProperty)

person magritte    schedule 09.10.2012    source источник


Ответы (3)


Что мне здесь не хватает?

Ничего такого. К сожалению, это так. Если вы укажете имя шаблона при вызове Html.EditorFor или использовании UIHint, шаблон будет вызываться для списка, а не для каждого элемента.

При этом вы, конечно, можете написать собственный метод расширения, который будет выполнять эту функциональность:

public static class HtmlExtensions
{
    private class ViewDataContainer: IViewDataContainer
    {
        public ViewDataContainer(ViewDataDictionary viewData)
        {
            ViewData = viewData;
        }

        public ViewDataDictionary ViewData { get; set; }
    }

    public static IHtmlString EditorForCollection<TModel, TProperty>(
        this HtmlHelper<TModel> html, 
        Expression<Func<TModel, IList<TProperty>>> expression
    )
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
        if (string.IsNullOrEmpty(metadata.TemplateHint))
        {
            return html.EditorFor(expression);
        }

        var collection = metadata.Model as IList<TProperty>;

        var sb = new StringBuilder();
        for (int i = 0; i < collection.Count; i++)
        {
            var indexExpression = Expression.Constant(i, typeof(int));
            var itemGetter = expression.Body.Type.GetProperty("Item", new[] { typeof(int) }).GetGetMethod();
            var methodCallExpression = Expression.Call(expression.Body, itemGetter, indexExpression);
            var itemExpression = Expression.Lambda<Func<TModel, TProperty>>(methodCallExpression, expression.Parameters[0]);
            var result = html.EditorFor(itemExpression, metadata.TemplateHint).ToHtmlString();
            sb.AppendLine(result);
        }
        return new HtmlString(sb.ToString());
    }
}

который может работать со свойствами модели представления коллекции типов, которые украшены атрибутом UIHint:

public class MyViewModel
{
    [UIHint("SomeTypeTemplate")]
    public IList<ItemViewModel> Items { get; set; }
}

и на ваш взгляд:

@model MyViewModel
@Html.EditorForCollection(x => x.Items)

и ваш ~/Views/Shared/EditorTemplates/SomeTypeTemplate.cshtml теперь можно было ввести в один ItemViewModel:

@model ItemViewModel
...

Вам больше не нужен промежуточный шаблон отображения, в котором вы бы просто зацикливались и вызывали фактический шаблон - это было бы настоящей тратой.

person Darin Dimitrov    schedule 10.10.2012

Вы создаете 2 EditorTemplates. Один обрабатывает IEnumerable, и этот шаблон циклически создает

Html.EditorFor(m => m.SomeProperty, "SomeIndividualTemplate")

для каждого элемента перечисления.

person simon831    schedule 10.10.2012

Хотя ответ @Darin идеален, по некоторым причинам, которые мне еще предстоит найти, TemplateHint из ModelMetaData всегда был нулевым. В качестве решения я создал еще одну перегрузку кода @Darin, чтобы принять параметр имени шаблона и отобразить представление вместо проверки TemplateHint в ModelMetaData. Извините, что публикую это как ответ, так как у меня нет прав на публикацию в качестве комментария. Спасибо Дарин :)

person ManojAnavatti    schedule 04.12.2014