如何基于XML文件在MVC 3中动态创建控件

时间:2011-06-13 10:52:00

标签: asp.net-mvc asp.net-mvc-3

我有一个XML文件存储在数据库中作为XML格式,其中包含一些控件,如下拉文本框,标签文本区域等,可能有也可能没有初始值。所以我的目标是读取XML文件并基于控件类型,我需要动态创建该控件并关联初始值(如果有的话)并且必须在视图中显示页面的预览。任何人请帮助我如何在MVC 3中为此方案动态创建控件。

例如:我的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>

提前致谢。

3 个答案:

答案 0 :(得分:49)

我会尝试为您提供一些可能会给您一些想法的提示。

与往常一样,我们首先定义一个代表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)
    

到目前为止,这么好。在此阶段,您应该能够呈现包含动态控件的表单。但是当然这样的形式对任何人来说都是无用的。什么是好的是有可能POST这个表单并捕获用户在控制器操作中输入的值,以便我们可以处理它们。

控制器操作如下所示:

[HttpPost]
public ActionResult Index(MyViewModel model)
{
    ... process the values
}

现在这样会很好但当然它不会起作用,因为ControlViewModel视图模型是一个抽象类,默认模型绑定器不知道要实例化哪个特定实现。所以我们需要帮助他=&gt;通过编写自定义模型绑定器:

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());

答案 1 :(得分:0)

事实上似乎很简单。从数据库中获取XML,解析它并将其放入某些模型类中。那些分类将包含控制数据。

接下来创建部分视图,它将根据模型动态构建所需的控件。

最后,从jQuery ajax调用该操作并将返回的HTML放在适当的位置。

快速,简单,没有真实......

答案 2 :(得分:0)

你可以将模型传递给强类型视图,并在帮助程序中编写一个名为Html.ControlFor的html帮助程序,你可以读取xml文件并根据属性名称的匹配创建控件(输入,选择等)(其中属性名称)匹配xml文件中的属性标记)