そのモデルが IEnumerable<SomeModel> であるという MVC 部分ビューがあります。
したがって、Html タイプは HtmlHelper<IEnumerable<SomeModel>> です
。ViewModel のインスタンスで公開するカスタム WebViewPage があります。
public class SomeModel
{
[Display("Model Id")]
public int Id { get; set; }
[Display("The Name")]
public string Name { get; set; }
}
public abstract class ViewModelWebViewPage : WebViewPage { }
public abstract class ViewModelWebViewPage<TModel> : ViewModelWebViewPage
{
...
public ViewModel<TModel> { get; set; }
}
この背後にある基本的な前提は、C# ViewModel 定義から、Knockout や JQuery Templating などの Javascript テンプレート システムのクライアント側テンプレート/バインディング式を駆動することです。
テーブル ヘッダーの DisplayName を取得するのに役立ついくつかのメソッドを作成しています (基本的には Display 属性の値 - 実際のバージョンでは、サブクラス化された DataAnnotationsMetadataProvider と IMetadataAware 属性を使用したローカリゼーションが含まれます)。
public class ViewModel<TModel>
{
public MvcHtmlString DisplayNameFor<TProperty>( Expression<Func<TModel, TProperty>> expression )
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression( expression, this.html.ViewData );
return new MvcHtmlString(metadata.DisplayName);
}
public MvcHtmlString DisplayNameFor<TItem, TProperty>( Expression<Func<TModel, IEnumerable<TItem>>> expression, Expression<Func<TItem, TProperty>> itemExpression )
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression( itemExpression, new ViewDataDictionary<TItem>() );
return new MvcHtmlString(metadata.DisplayName);
}
}
ビューでは、このヘルパーを次のように使用します...
@model IEnumerable<SomeModel>
<table>
<thead>
<tr>
<!--
"this" is a ViewModelViewWebPage<IEnumerable<SomeModel>>
ViewModel is a ViewModel<IEnumerable<SomeModel>>
m is an IEnumerable<SomeModel>,
item is of type SomeModel
-->
<th>@ViewModel.DisplayNameFor( m => m, item => item.Id )</th>
<th>@ViewModel.DisplayNameFor( m => m, item => item.Name )</th>
</tr>
</thead>
... remainder of table html
</table>
これはすべて機能し、レンダリングされた HTML で期待される結果が得られます
<table>
<thead>
<tr>
<th>Model Id</th>
<th>The Name</th>
</tr>
</thead>
... remainder of table html
</table>
ViewModel<TModel> が実際には ViewModel であることを理解できる ViewModel<TModel> クラスでメソッドを定義したいので、最初の式で現在のモデルを渡して 2 番目の式をブートストラップする必要があるのは好きではありません<IEnumerable<TItem>>
<th>@ViewModel.DisplayNameFor( m => m.Id )</th>
拡張メソッドを作成することができました
public static MvcHtmlString DisplayNameFor<TItem, TValue>(this ViewModel<IEnumerable<TItem>> viewModel, Expression<Func<TItem, TValue>> expression)
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, new ViewDataDictionary<TItem>());
return new MvcHtmlString(metadata.DisplayName);
}
しかし、ViewModel クラス自体の一部にしたいのですが、DisplayNameFor メソッドを定義して IEnumerable 関係を推測する方法を見つけることができるようです。
私は試した
public MvcHtmlString DisplayNameFor<TItem, TValue>(Expression<Func<TItem, TValue>> expression)
where TModel : IEnumerable<TItem>
{
ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, new ViewDataDictionary<TItem>());
return new MvcHtmlString(metadata.DisplayName);
}
ただし、制約からの TModel は囲んでいる型に属していないため、コンパイルされませんが、メソッドへの Type パラメーターとして期待されます。
ViewModel<TModel> クラス内で、TModel が IEnumerable<TItem> 関係であることを強制できるようなメソッド シグネチャを定義して、式の引数を Func<TItem, TValue> にすることは可能ですか?拡張メソッドと一緒に暮らす。