29

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.

5
  • How does your XML file look like? Can you provide an example? Commented Jun 13, 2011 at 11:02
  • What about default values in the XML? What about dropdown lists? How are the values as well as the selected value going to be stored? Commented Jun 13, 2011 at 12:49
  • see my blog on this blogs.msdn.com/b/rickandy/archive/2012/01/09/… Commented Feb 17, 2012 at 20:05
  • @BonDavid how did you managed to do this case finally? Have you got any demo to download? Commented Jan 17, 2014 at 14:31
  • @DarinDimitrov can you help me a litle please Commented Sep 5, 2014 at 11:47

3 Answers 3

50

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());
Sign up to request clarification or add additional context in comments.

8 Comments

Can you please tell how to validate these dynamically created controls.. i followed what u did and made a form with dynamically created controls..But i want to validate these controls when the user submits the form. how can I achieve that..?
You might Add HtmpProperties to the Controls one of them containing some OnChange-Validation
@SandeepChauhan can you share your example.
@Darin Not compatible with MVC4?
@DarinDimitrov my mistake. sorry for bothering you.
|
0

It seems to be quite simple in fact. Get your XML from the database, parse it and put it into some model classes. Those classed will contains controls data.

Next create partial view which will dynamicly build required controls depending on model.

At the end, call that action from jQuery ajax and put returned HTML in the proper place.

Quick, easy, no reaload....

1 Comment

One quick thought, could you use XSLT to transform it into what you want the HTML to look like, may be a quick way to do it in one shot?
0

you can pass a model to your strongly typed view andwrite a html helper named Html.ControlFor in the helper you can read the xml file and create controls (input, select etc.) based on match of property name (where property name matches property tag in xml file)

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.