8

在 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)
4

3 回答 3

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
...

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

于 2012-10-10T09:26:19.873 回答
3

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

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

对于枚举中的每个项目。

于 2012-10-10T09:14:15.913 回答
1

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

于 2014-12-04T10:23:26.610 回答