如何基于XML文件在MVC 3中动态创建控件 [英] How to create controls dynamically in MVC 3 based on an XML file

查看:20
本文介绍了如何基于XML文件在MVC 3中动态创建控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

例如:我的 xml 文件看起来像这样.

<property name="Visible" value="true"/><属性名称=ID"值=标签1"/>...</类型></控制><控制><type name="TextBox"><property name="Visible" value="true"/><property name="ID" value="TextBox1"/>...</类型></控制>...</控制>

提前致谢.

解决方案

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

和往常一样,让我们​​首先定义一个表示 UI 的视图模型:

公共类 MyViewModel{公共 ControlViewModel[] 控件 { 获取;放;}}公共抽象类 ControlViewModel{公共抽象字符串类型{获取;}公共布尔可见{得到;放;}公共字符串标签{获取;放;}公共字符串名称 { 获取;放;}}公共类 TextBoxViewModel : ControlViewModel{公共覆盖字符串类型{得到 { 返回文本框";}}公共字符串值 { 获取;放;}}公共类 CheckBoxViewModel : ControlViewModel{公共覆盖字符串类型{得到 { 返回复选框";}}公共布尔值{得到;放;}}公共类 DropDownListViewModel :TextBoxViewModel{公共覆盖字符串类型{得到{返回ddl";}}公共选择列表值 { 获取;放;}}

所以我们已经定义了一些我们想要在我们的应用程序中处理的基本控件.下一步是拥有一个存储库方法,它将查询数据库,获取 XML,然后是一个映射层,最终为我们提供视图模型的实例.我将其视为超出此答案的范围,因为您可以通过多种方式实现它(XmlSerializer、XDocument、XmlReader 等).

我想你已经有了一个视图模型的实例.像这样:

public ActionResult Index(){var 模型 = 新的 MyViewModel{控件 = 新的 ControlViewModel[]{新的文本框视图模型{可见 = 真,标签 = "标签 1",Name = "TextBox1",Value = "文本框的值"},新的 CheckBoxViewModel{可见 = 真,Label = "检查标签",Name = "CheckBox1",值 = 真},新的下拉列表视图模型{可见 = 真,Label = "删除标签",Name = "DropDown1",值 = 新的 SelectList(新的[]{新 { 值 = "1", 文本 = "文本 1" },新 { 值 = "2", 文本 = "文本 2" },新 { 值 = "3", 文本 = "文本 3" },}, "值", "文本", "2")}}};返回视图(模型);}

所以我已经硬编码了一些值来说明这个概念,但通常一旦你实现了存储库和映射层,这个控制器操作看起来像这样:

public ActionResult Index(){字符串 xml = _repository.GetControls();var model = Mapper.Map(xml);返回视图(模型);}

好的,现在让我们转到包含表单的相应 Index.cshtml 视图:

@model MyViewModel@using (Html.BeginForm()){for (int i = 0; i  x.Controls[i].Type)@Html.HiddenFor(x => x.Controls[i].Name)@Html.EditorFor(x => x.Controls[i])

}}<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]公共 ActionResult 索引(MyViewModel 模型){... 处理值}

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

公共类 ControlModelBinder : DefaultModelBinder{受保护的覆盖对象 CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType){var type = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");对象模型 = 空;开关(type.AttemptedValue){案例文本框":{模型 = 新的 TextBoxViewModel();休息;}案例复选框":{模型 = 新的 CheckBoxViewModel();休息;}案例ddl":{模型 = 新 DropDownListViewModel();休息;}默认:{抛出新的 NotImplementedException();}};bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType());退货模式;}}

将在 Application_Start 中注册并关联到 ControlViewModel 类型):

ModelBinders.Binders.Add(typeof(ControlViewModel), new ControlModelBinder());

I have a XML file stored in the database as XML format which contains some controls like drop down text box, label text area, etc. which may or may not have initial values. So my aim is to read the XML file and based on the control type, I need to create that control dynamically and associate the initial value if any and the preview of the page have to be shown in a view. Anybody please help me how to create the controls dynamically in MVC 3 for this scenario.

For eg: my xml file will look something like this.

<?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>

Thanks in advance.

解决方案

I will try to provide you with some hints that might give you some ideas.

As always let's start by defining a view model which will represent the 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; }
}

So we have defined some of the basic controls we would like to handle in our application. The next step would be to have a repository method which will query the database, fetch the XML and then a mapping layer that will finally provide us with an instance of our view model. I am leaving this as out of scope for this answer as there are many ways you could implement it (XmlSerializer, XDocument, XmlReader, ...).

I suppose that you already have an instance of the view model. Like this:

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

So I have hardcoded some values in order to illustrate the concept, but normally this controller action would look something like this once you implement the repository and mapping layer:

public ActionResult Index()
{
    string xml = _repository.GetControls();
    var model = Mapper.Map<string, MyViewModel>(xml);
    return View(model);
}

OK, now let's move to the corresponding Index.cshtml view which will contain the form:

@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, so now we can define the corresponding editor templates for the controls we would like to handle:

  • ~/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)
    

So far, so good. At this stage you should be able to render a form containing the dynamic controls. But of course such a form is pretty useless to anyone. What would be nice is to have the possibility of POSTing this form and capturing the values entered by the user in a controller action so that we could process them.

The controller action would look like this:

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

Now that would be nice but of course it won't work because the ControlViewModel view model is an abstract class and the default model binder has no clue about which specific implementation to instantiate. So we need to help him => by writing a custom model binder:

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;
    }
}

which will be registered in Application_Start and associated to the ControlViewModel type):

ModelBinders.Binders.Add(typeof(ControlViewModel), new ControlModelBinder());

这篇关于如何基于XML文件在MVC 3中动态创建控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆