How to design MForm at run time?

Jul 8, 2010 at 7:46 AM

How to design MForm control at run time?

Coordinator
Jul 8, 2010 at 7:59 PM
Edited Jul 8, 2010 at 9:17 PM

MForm heavily uses the concept of ASP.NET templates. If you want to create an MForm at run time, you have to create the Root control and create a class that implements the ITemplate interface and than assign an instance of this class to root's content property. You could for example create such templates:

public class BranchTemplate : ITemplate 
{
   public string Name { get; set: }
   public ITemplate Contents { get; set: }

   public void InstantiateIn(Control control)
   {
      control.Controls.Add(new Branch{Name = Name, Contents = Contents});
   }
}

public class LeafTemplate : ITemplate 
{
   public string Name { get; set: }

   public void InstantiateIn(Control control)
   {
      control.Controls.Add(new Leaf{Name = Name});
   }
}

public class MergedTemplate : ITemplate
{        
   public IEnumerable<ITemplate> Templates { get; set;}

   public void InstantiateIn(Control container)
   {
      foreach (var template in Templates.Where(template => template != null))
      {
         template.InstantiateIn(container);
      }
   }
}

and then use these templates like this:

var root = new Root();
root.Contents = 
new BranchTemplate
   {
       Name = "Test"
       Contents = new MergedTemplate{
          Templates = new ITemplate[] 
          {
             new BranchTemplate
                {
                   Name = "Inner"
                },
              new LeafTemplate
                {
                   Name = "Leaf"
                }
          }
   }

(there might be some mistakes as I am writing everything from the top of my head)

Now if you want to generate MForm at runtime from a specific xml schema, you might look into the BM.Tools.WebControls.MForm.Design and BM.Tools.WebControls.MForm.Generator namespaces. You might look especially into the GeneratorForm.cs class and try to understand the GetFormItem method (the output of this method is used by the method GetGeneratedData which further is used by the RootActionList class in the GenerateRootContentsAndUriMappings method). Here is a simplified excerpt from those classes:

// assume that we have an XmlSchemaSet "set" and the global element from this set has XmlQualifiedName "elementName"
var formBuilder = new FormBuilder(set);
// you might set some formBuilder seetings by specifying formBuilder.Settings
var element = (XmlSchemaElement)set.GlobalElements[elementName];

var rootItem = formBuilder.TraverseElement(element) as FormItem;
var root = new Root();
root.Contents = new ItemTemplate(rootItem);
root.Manager = rootItem.Builder.Manager;

What is happening in here is that the xml schema is read using the FormBuilder class. The FormBuilder class inherits from the SchemaTraverser class which is a class that uses the visitor pattern on the Xml schema objects. The output of schema traversing is a FormItem object which essentially is an object that facilitates reading xml schema data (e.g. it is easy in the FormItem object to find what xml schema element children are in the xml schema element that the FormItem is bound to).

Another useful feature of the FormItem object is the possibility to enable / disable a piece of the xml schema tree. Each of the FormItem objects has a "Enabled" property. If this property is set to false, the given FormItem is not rendered.

Finally the FormItem object is consumed by the ItemTemplate template class, which reads data from the FormItem object and creates appropriate data in the template. You can think of the ItemTemplate class as something a little bit more complicated than the BranchTemplate / LeafTemplate classes that I have written in the beginning.

What is important is that:

  • You are not forced to use the FormBuilder / ItemTemplate combo, although it will give you the same effect as generating forms in design time;
  • When creating your own templates, you are not limited to MForm controls. You can for example create a System.Web.UI.WebControls.Table control and than inside place a branch, leaf or choice MForm controls;
  • If you need MForm generated at runtime only for automatic webservice tests or something like that, then it is a good idea to do it (in fact in our company we use it as a simple web service web page entrypoint). But if you want to generate the forms at run time for well defined pages, because you want to save a little time on using the designer, then this might not be the best idea. First, when you generate the form at design time you are free to modify it later, specify display names, set css classes, move items, add constraints, set javascript events and so much more, but to have the same effect when generating at runtime, you will have to make tour template class complicated and bloated, adding another hack to meet the requirement. Second, reading the xml schema every time you want to generate the form, then visiting the xml schema using the FormBuilder and finally creating the template from the FormItem is quite a time consuming process. Of course you could do some caching but I am sure that eventually you will have more coding than using the design time approach in the first place.

If you have any more questions, please do not hesitate and drop me a line

Kind regards, Michal
Jul 9, 2010 at 1:29 AM
Edited Jul 9, 2010 at 8:21 AM

Hi, Michal:

thank you for your response.
what i want is to generate MForm at runtime from a specific xml schema, and with your help,  I have realized it, but  it doesn't work so well, could you give me a demo with input and output funtions?

thanks a lot