フロアプランに追加されているテーブルを一覧表示するビューがあります。テーブルはTableInputModel
から派生し、RectangleTableInputModel
、CircleTableInputModel
などを考慮します。
ViewModelには、すべて派生型の1つであるTableInputModel
のリストがあります。
派生型ごとに部分的なビューがあり、混合派生型のList
が与えられると、フレームワークはそれらをレンダリングする方法を知っています。
ただし、フォームを送信すると、タイプ情報は失われます。カスタムモデルバインダーを試してみましたが、送信時にタイプ情報が失われるため、機能しません...
誰かがこれを以前に試したことがありますか?
次のモデルがあると仮定します。
public abstract class TableInputModel
{
}
public class RectangleTableInputModel : TableInputModel
{
public string Foo { get; set; }
}
public class CircleTableInputModel : TableInputModel
{
public string Bar { get; set; }
}
そして、次のコントローラー:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new TableInputModel[]
{
new RectangleTableInputModel(),
new CircleTableInputModel()
};
return View(model);
}
[HttpPost]
public ActionResult Index(TableInputModel[] model)
{
return View(model);
}
}
これで、ビューを作成できます。
メインビューIndex.cshtml
:
@model TableInputModel[]
@using (Html.BeginForm())
{
@Html.EditorForModel()
<input type="submit" value="OK" />
}
および対応するエディターテンプレート。
~/Views/Home/EditorTemplates/RectangleTableInputModel.cshtml
:
@model RectangleTableInputModel
<h3>Rectangle</h3>
@Html.Hidden("ModelType", Model.GetType())
@Html.EditorFor(x => x.Foo)
~/Views/Home/EditorTemplates/CircleTableInputModel.cshtml
:
@model CircleTableInputModel
<h3>Circle</h3>
@Html.Hidden("ModelType", Model.GetType())
@Html.EditorFor(x => x.Bar)
パズルの最後の欠けている平和は、TableInputModel
型のカスタムモデルバインダーです。これは、投稿された非表示フィールド値を使用して型をフェッチし、適切な実装をインスタンス化します。
public class TableInputModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".ModelType");
var type = Type.GetType(
(string)typeValue.ConvertTo(typeof(string)),
true
);
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
Application_Start
に登録されます:
ModelBinders.Binders.Add(typeof(TableInputModel), new TableInputModelBinder());
そしてそれはほとんどすべてです。これで、Index Postアクション内で、モデル配列が正しい型で適切に初期化されます。
Mvccontribに "Derived Type Model Binder" がありました。しかし、残念ながら、mvccontribバージョン3にはそのようなバインダーはありません。