例如:我的xml文件将看起来像这样。
- <?xml version="1.0" encoding="utf-8" ?>
- <controls>
- <control>
- <type name="label">
- <property name="Visible" value="true"/>
- <property name="ID" value="Label1"/>
- .
- .
- .
- </type>
- </control>
- <control>
- <type name="TextBox">
- <property name="Visible" value="true"/>
- <property name="ID" value="TextBox1"/>
- .
- .
- .
- </type>
- </control>
- .
- .
- .
- </controls>
提前致谢。
解决方法
一如既往,我们首先定义一个代表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);
- }
OK,现在让我们移动到相应的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" />
- }
OK,所以现在我们可以为我们想要处理的控件定义相应的编辑器模板:
>〜/ Views / Shared / EditorTemplates / TextBoxviewmodel.cshtml
>〜/ Views / Shared / EditorTemplates / CheckBoxviewmodel.cshtml
>〜/ Views / Shared / EditorTemplates / DropDownListviewmodel.cshtml
- @model AppName.Models.DropDownListviewmodel
- @Html.LabelFor(x => x.Value,Model.Label)
- @Html.DropDownListFor(x => x.Value,Model.Values)
到现在为止还挺好。在这个阶段,你应该能够渲染一个包含动态控件的窗体。但是当然这样的形式对任何人都是没用的。什么是好的是有可能POSTing此表单和捕获用户输入的值在控制器操作,以便我们可以处理它们。
控制器操作将如下所示:
- [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());