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を作成します。1つはIEnumerableを処理し、このテンプレートはループして

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

列挙内の各項目について。

于 2012-10-10T09:14:15.913 に答える
1

@Darinの答えは完璧ですが、ModelMetaDataからTemplateHintをまだ見つけていないいくつかの理由で、常にnullでした。解決策として、ModelMetaDataでTemplateHintをチェックする代わりに、テンプレート名パラメーターを受け入れてビューをレンダリングするために、@Darinのコードのオーバーロードをもう1つ作成しました。コメントとして投稿する権限がないため、回答として投稿して申し訳ありません。ありがとうダリン:)

于 2014-12-04T10:23:26.610 に答える