例如:我的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
@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)
到现在为止还挺好。在这个阶段,你应该能够渲染一个包含动态控件的窗体。但是当然这样的形式对任何人都是没用的。什么是好的是有可能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());