我将尝试为您提供一些提示,可能会给您一些想法。
和往常一样,让我们从定义一个代表 UI 的视图模型开始:
public class MyViewModel
{
public ControlViewModel[] Controls { get; set; }
}
public abstract class ControlViewModel
{
public abstract string Type { get; }
public bool Visible { get; set; }
public string Label { get; set; }
public string Name { get; set; }
}
public class TextBoxViewModel : ControlViewModel
{
public override string Type
{
get { return "textbox"; }
}
public string Value { get; set; }
}
public class CheckBoxViewModel : ControlViewModel
{
public override string Type
{
get { return "checkbox"; }
}
public bool Value { get; set; }
}
public class DropDownListViewModel : TextBoxViewModel
{
public override string Type
{
get { return "ddl"; }
}
public SelectList Values { get; set; }
}
所以我们已经定义了一些我们希望在我们的应用程序中处理的基本控件。下一步将是有一个存储库方法,它将查询数据库、获取 XML,然后是一个映射层,最终将为我们提供视图模型的实例。我将其排除在此答案的范围之外,因为您可以通过多种方式实现它(XmlSerializer、XDocument、XmlReader,...)。
我想你已经有一个视图模型的实例。像这样:
public ActionResult Index()
{
var model = new MyViewModel
{
Controls = new ControlViewModel[]
{
new TextBoxViewModel
{
Visible = true,
Label = "label 1",
Name = "TextBox1",
Value = "value of textbox"
},
new CheckBoxViewModel
{
Visible = true,
Label = "check label",
Name = "CheckBox1",
Value = true
},
new DropDownListViewModel
{
Visible = true,
Label = "drop label",
Name = "DropDown1",
Values = new SelectList(
new[]
{
new { Value = "1", Text = "text 1" },
new { Value = "2", Text = "text 2" },
new { Value = "3", Text = "text 3" },
}, "Value", "Text", "2"
)
}
}
};
return View(model);
}
所以我已经硬编码了一些值来说明这个概念,但是一旦你实现了存储库和映射层,这个控制器操作通常看起来像这样:
public ActionResult Index()
{
string xml = _repository.GetControls();
var model = Mapper.Map<string, MyViewModel>(xml);
return View(model);
}
好的,现在让我们转到相应的Index.cshtml 视图,其中将包含表单:
@model MyViewModel
@using (Html.BeginForm())
{
for (int i = 0; i < Model.Controls.Length; i++)
{
if (Model.Controls[i].Visible)
{
<div>
@Html.HiddenFor(x => x.Controls[i].Type)
@Html.HiddenFor(x => x.Controls[i].Name)
@Html.EditorFor(x => x.Controls[i])
</div>
}
}
<input type="submit" value="OK" />
}
好的,现在我们可以为我们想要处理的控件定义相应的编辑器模板:
-
~/Views/Shared/EditorTemplates/TextBoxViewModel.cshtml
@model AppName.Models.TextBoxViewModel
@Html.LabelFor(x => x.Value, Model.Label)
@Html.TextBoxFor(x => x.Value)
-
~/Views/Shared/EditorTemplates/CheckBoxViewModel.cshtml
@model AppName.Models.CheckBoxViewModel
@Html.CheckBoxFor(x => x.Value)
@Html.LabelFor(x => x.Value, Model.Label)
-
~/Views/Shared/EditorTemplates/DropDownListViewModel.cshtml
@model AppName.Models.DropDownListViewModel
@Html.LabelFor(x => x.Value, Model.Label)
@Html.DropDownListFor(x => x.Value, Model.Values)
到目前为止,一切都很好。在这个阶段,您应该能够呈现包含动态控件的表单。但当然,这样的形式对任何人来说都是毫无用处的。最好有可能发布此表单并捕获用户在控制器操作中输入的值,以便我们可以处理它们。
控制器动作如下所示:
[HttpPost]
public ActionResult Index(MyViewModel model)
{
... process the values
}
现在这很好,但它当然不会工作,因为ControlViewModel 视图模型是一个抽象类,默认模型绑定器不知道要实例化哪个具体实现。所以我们需要帮助他 => 通过编写自定义模型绑定器:
public class ControlModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var type = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");
object model = null;
switch (type.AttemptedValue)
{
case "textbox":
{
model = new TextBoxViewModel();
break;
}
case "checkbox":
{
model = new CheckBoxViewModel();
break;
}
case "ddl":
{
model = new DropDownListViewModel();
break;
}
default:
{
throw new NotImplementedException();
}
};
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());
return model;
}
}
将在Application_Start中注册并关联到ControlViewModel类型):
ModelBinders.Binders.Add(typeof(ControlViewModel), new ControlModelBinder());