如何使用具有IEnumerable <t> </t>类型属性的UIHint创建EditorTemplate

时间:2012-10-09 10:56:54

标签: c#-4.0 razor asp.net-mvc-4 mvc-editor-templates

在MVC中,您可以为T创建一个编辑器模板,然后当您想要为IEnumerable<T>类型的属性呈现编辑器时,您可以简单地执行此操作。

Html.EditorFor(m => m.MyListOfT)

这样做的好处是,输入框架会自动创建名称,然后在回发模型绑定时,所有名称都能正常工作。

我的问题是:当您有多种类型的编辑器模板时,如何执行上述操作?

我尝试过使用UIHint(),但它似乎只允许您根据列表指定UIHint,而不是列表中的每个项目。这意味着你必须使用foreach()循环为列表创建一个EditorTemplate,然后你错过了漂亮的自动命名和模型绑定。

我在这里缺少什么?

该模型是例如

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)

3 个答案:

答案 0 :(得分:10)

  

我在这里缺少什么?

无。不幸的是,它是如何的。如果在调用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
...

您不再需要中间显示模板,您只需循环并调用实际模板 - 这将是一个真正的浪费。

答案 1 :(得分:3)

您创建了2个EditorTemplates。一个处理IEnumerable,这个模板循环创建一个

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

枚举中的每个项目。

答案 2 :(得分:1)

尽管@ Darin的答案是完美的,但由于某些原因,我还没有发现ModelMetaData中的TemplateHint始终为null。作为解决方案,我创建了一个@ Darin代码的重载,以接受模板名称参数并呈现View而不是在ModelMetaData中检查TemplateHint。很抱歉将其发布为答案,因为我没有权限发布评论。谢谢达林:)