【问题标题】:How to create controls dynamically in MVC 3 based on an XML file如何基于 XML 文件在 MVC 3 中动态创建控件
【发布时间】:2011-06-13 10:51:59
【问题描述】:

我有一个 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>

提前致谢。

【问题讨论】:

  • 您的 XML 文件是什么样的?你能举个例子吗?
  • XML 中的默认值怎么样?下拉列表呢?如何存储这些值以及选定的值?
  • @BonDavid 你最后是怎么处理这个案子的?你有任何演示要下载吗?
  • @DarinDimitrov 你能帮帮我吗

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


【解决方案1】:

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

和往常一样,让我们​​从定义一个代表 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());

【讨论】:

  • 你能告诉我如何验证这些动态创建的控件。我按照你所做的,用动态创建的控件制作了一个表单。但是我想在用户提交表单时验证这些控件。我怎样才能做到这一点..?
  • 您可以将 HtmpProperties 添加到其中一个包含一些 OnChange-Validation 的控件中
  • @SandeepChauhan 你能分享你的例子吗?
  • @Darin 不兼容 MVC4?
  • @DarinDimitrov 我的错误。很抱歉打扰您。
【解决方案2】:

其实看起来很简单。从数据库中获取您的 XML,对其进行解析并将其放入一些模型类中。那些分类将包含控制数据。

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

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

快速、简单、无需重新加载......

【讨论】:

  • 快速思考一下,您能否使用 XSLT 将其转换为您想要的 HTML 外观,可能是一种快速完成的方法?
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    • 2012-12-29
    相关资源
    最近更新 更多