web-dev-qa-db-ja.com

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)を呼び出すことができますか?

43
Dismissile

このような:

@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>
64
Darin Dimitrov

これはMaslowのコメントへの返信です。これは私のSOへの初めての貢献なので、コメントするのに十分な評判がないため、回答としての返信になります。

ModelMetadataProviderで 'TemplateHint'プロパティを設定できます。これは、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;
    }

基本的には、「CachedDataAnnotationsModelMetadataProvider」の「CreateMetadataFromPrototype」メソッドをオーバーライドし、派生型を優先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」を指定する必要はありません。

これが何らかの価値があることを願っています。

8
swazza85

ビューから出力を取得しないことについての私の質問では、実際に子モデルのコレクションでモデルをテンプレート化し、それらすべてをレンダリングする方法の例があります。

ASP.NET表示テンプレート-出力なし

基本的に、List<T>またはCollection<T>をサブクラス化するモデルを作成し、これを使用する必要があります。

@model ChildModelCollection 

@foreach (var child in Model)
{
    Html.DisplayFor(m => child);
}

コレクションモデルのテンプレートで、子を反復してレンダリングします。各子は強く型付けする必要があるため、アイテムの独自のモデルタイプを作成し、それらのテンプレートを用意することもできます。

OPの質問の場合:

public class ChildModelCollection : Collection<ChildModel> { }

他のようにテンプレートに解決できるコレクションである強く型付けされたモデルを作成します。

8
Luke Puplett

実際の「有効な回答」は-IMHO-が質問に正しく回答しないことです。 OPは、UIHintを指定せずにトリガーするリストテンプレートを作成する方法を探していると思います。

魔法のものはほとんど仕事をします

一部のマジックは、指定されたタイプの正しいビューをロードします。
さらに魔法の負荷同じビュー for のコレクション指定されたタイプ。
そこにはすべき指定されたタイプのコレクションに対して同じビューを繰り返す魔法があります。

実際の動作を変更しますか?

お気に入りの逆アセンブラを開きます。魔法は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>
    }
}

現在、DisplayForUIHintなしで機能します。

2
SandRock