An Introduction to the MForm Web Controls

The source code

The project described in this article is hosted on mform.codeplex.com.

You will find the most recent version (the project is recently updated) here

Introduction  

It is most likely that you have seen probably the best beer in the World commercial. Less chances are that you have heard of the MForm webcontrols. And if you are like me a web developer struggling with writing web forms that collect and process loads of data, you surely might find the described controls interesting.

Project background 

The MForm webcontrols project has been started in the IT departament of Bank Millennium, Poland, a place where I work. It has been recently published under the Microsoft public license.

If you have ever applied for a loan or a mortgage, you already know that banks want to know as much as possible about their clients to minimize the risk of giving too much money to a person that cannot afford returning it in the future. All of this data is filled in by bank consultants into bank's intranet application during a meeting with the client.

The duty of a bank IT developer is then to:

  • define the data contract (a data structure and data restrictions) for the information that the business department wants to collect; 
  • create a web form (or several web forms) that collects the data according to the data contract;
  • validate the fields in the web form against data restrictions;
  • combine the data from the fields into the data structure;

Finally the data is sent somewhere else, where it is used to make a decision about the client.

What struck me, was that all of these tasks looked like they could be made at least partially automatically. The forms could be generated directly from the data contract (in our case the data contract was defined using XML schema), the validations (at least those basic ones as checking field requirement, its maximal length, matching a pattern or maximal value) could be also taken from the data contract, finally the data combining (creating an output XML instead of a collection of fields) could be done automatically.

Still, the solution had to be extensible enough to:

  • allow modifying the created forms visually after the generation;
  • allow adding custom business validations that would work the same way data validations work; 
  • make the process of creating the forms as easy as possible, so that even an unexperienced developer could prepare at least a web form sketch;
  • be efficient and use Ajax to eliminate unnecessary PostBacks;
  • use generic ASP.NET controls and solutions wherever possible;

An Introduction to the MForm Web Controls

Generating your first MForm web form 

1. Get the contract.  

To be able to generate a form, you first need to have data definitions. It is best if you have these definitions in XML schema (as XML schema provides a way to declaratively define data restrictions not currently available in code), but if you are not familiar with XML schema, you can also generate a form from a managed library.

In the following example we will use a Customer data definition:

[generation_1.png]

The Customer has:

  • a first and last names, which cannot be longer than 20 characters,
  • national id,
  • a birth date (of type date),  
  • sex, which can be only of value 'Male' or 'Female'
  • and the address, which consists of:
    • street of maximal length 100, 
    • house number of maximal length 10, 
    • flat number, which is optional, 
    • post code, which has to match the pattern two digits - three digits,
    • city of maximal length 50 

Assuming that you have already downloaded the project code, you will find the Customer data definition in the BM.Tools.WebControls.MForm.Example/Schemas/Customer.xsd file.

An Introduction to the MForm Web Controls

2. Create a page with the MForm Root control 

  1. Open the project in Visual Studio 2008.
  2. Create a new item -> Web Content Form in the BM.Tools.WebControls.MForm.Example project
  3. Choose Site.Master as the Master page
  4. A page will open and will look like this:
    <%@ Page 
    	Title="" 
    	Language="C#" 
    	MasterPageFile"~/Site.Master" 
    	AutoEventWireup="true" 
    	CodeBehind="WebForm1.aspx.cs" 
    	Inherits="BM.Tools.WebControls.MForm.Example.WebForm1" 
    
    %>
    	<asp:content runat="server"
            contentplaceholderid="ContentPlaceHolder1" 
            id="Content1">
    </asp:content>
    
  5. Switch to design mode
  6. Find the Root control in the toolbox
  7. Drop the Root control on the page. It should look like this:
    [generation_3.png]
  8.  Change the designer mode to Edit

3. Click the load a root template from schema button 

You will see the generator form:

[generation_4.png]

An Introduction to the MForm Web Controls

4. Choose the contract source and location 

Click the choose button and select the XML schema, wsdl or assembly file. Then click Open
The Element name dropdown list will be propagated with available elements (One XML schema file may have more than one element definition, same as one assembly may have more than one class)

[generation_5.png]

5. Select the element 

Once you select one of the elements from the dropdown list, the element tree will be displayed. You have a possibility to decide which definitions should be rendered in the form. By default the whole tree is prepared to be rendered. To change this, uncheck the checkboxes near the elements:

[generation_6.png]

6. Click Generate 

Once you click Generate, the Root control is filled in with the content. The Root designer mode is changed to View mode so you can see the result:
(The rendering in the designer mode is not perfect, the actual page display may vary ): 

[generation_7.png]

An Introduction to the MForm Web Controls

7. Switch to Source View 

The generated form is available in the source view and can be freely modified. We will take a closer look at the generated code:

<%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true"

    CodeBehind="WebForm1.aspx.cs" Inherits="BM.Tools.WebControls.MForm.Example.WebForm1" %>

<%@ Register Assembly="BM.Tools.WebControls.MForm" Namespace="BM.Tools.WebControls.MForm.Controls"
    TagPrefix="mf" %>

<%@ Register Assembly="BM.Tools.WebControls.MForm" Namespace="BM.Tools.WebControls.MForm.Controls.Additions"
    TagPrefix="mfadd" %>
<%@ Register Assembly="BM.Tools.WebControls.MForm" Namespace="BM.Tools.WebControls.MForm.Controls.ValueHandlers"

    TagPrefix="mfvh" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <mf:Root ID="Root1" runat="server" Name="Root1">

        <UriMappings>
            <mf:UriMapping Namespace="http://www.w3.org/2001/XMLSchema" Prefix="xs" />
            <mf:UriMapping Namespace="" Prefix="" />

        </UriMappings>
        <Contents>
            <mf:Branch runat="server" Name="Customer">
                <Contents>
                    <mf:Leaf runat="server" Name="FirstName" Ordinal="1">

                        <Additions>
                            <mfadd:Restriction runat="server" RestrictionType="MaxLength" Value="20" />
                        </Additions>

                    </mf:Leaf>
                    <mf:Leaf runat="server" Name="Surname" Ordinal="2">
                        <Additions>
                            <mfadd:Restriction runat="server" RestrictionType="MaxLength" Value="20" />

                        </Additions>
                    </mf:Leaf>
                    <mf:Leaf runat="server" Name="Sex" Ordinal="3">
                        <ValueHandler>

                            <mfvh:ListBoxValueHandler runat="server" Value="">
                                <ListBox Rows="1">
                                    <asp:ListItem Value="Female">Female</asp:ListItem>
                                    <asp:ListItem Value="Male">Male</asp:ListItem>

                                </ListBox>
                            </mfvh:ListBoxValueHandler>
                        </ValueHandler>
                    </mf:Leaf>
                    <mf:Leaf runat="server" Name="NationalId" Ordinal="4">

                    </mf:Leaf>
                    <mf:Leaf runat="server" DataType="Date" Name="BirthDate" Ordinal="5">
                    </mf:Leaf>

                    <mf:Branch runat="server" Name="Address" Ordinal="6">
                        <Contents>
                            <mf:Leaf runat="server" Name="Street" Ordinal="1">

                                <Additions>
                                    <mfadd:Restriction runat="server" RestrictionType="MaxLength" Value="100" />
                                </Additions>

                            </mf:Leaf>
                            <mf:Leaf runat="server" Name="HouseNumber" Ordinal="2">
                                <Additions>
                                    <mfadd:Restriction runat="server" RestrictionType="MaxLength" Value="10" />

                                </Additions>
                            </mf:Leaf>
                            <mf:Leaf runat="server" MinOccurs="0" Name="FlatNumber" Ordinal="3">

                                <Additions>
                                    <mfadd:Restriction runat="server" RestrictionType="MaxLength" Value="10" />
                                </Additions>

                            </mf:Leaf>
                            <mf:Leaf runat="server" Name="PostCode" Ordinal="4">
                                <Additions>
                                    <mfadd:Restriction runat="server" RestrictionType="Pattern" Value="\d{2}-\d{3}" />

                                </Additions>
                            </mf:Leaf>
                            <mf:Leaf runat="server" Name="Post" Ordinal="5">
                                <Additions>

                                    <mfadd:Restriction runat="server" RestrictionType="MaxLength" Value="50" />
                                </Additions>
                            </mf:Leaf>

                            <mf:Leaf runat="server" Name="City" Ordinal="6">
                                <Additions>
                                    <mfadd:Restriction runat="server" RestrictionType="MaxLength" Value="50" />

                                </Additions>
                            </mf:Leaf>
                        </Contents>
                    </mf:Branch>
                </Contents>
            </mf:Branch>

        </Contents>
        <Validator Placement="BeforeContent"></Validator>
    </mf:Root>
</asp:Content>

The generated code in aspx may look a little scary at first. There is quite a lot of data that has been generated. Of course you are not needed to understand all of the generated code unless you want to change it somehow.

The Root control has three elements inside:  

  • UriMappings element
  • Contents element
  • Validator element

An Introduction to the MForm Web Controls

The UriMappings

The UriMappings element keeps data about prefix and namespace mappings in the data definition. In the case of our example, no namespaces were used, so there is only one mapping, an empty prefix is mapped to an empty namespace.

The Validator

The Validator element is responsible of displaying the form validation errors. It plugs the MForm tree in the ASP.NET page validation mechanism.

The Contents property

Finally, the Contents element is the most important part of the Root control. The Contents property is a template that is instantiated on page rendering, so it may contain any valid aspx script code: html code, web controls, etc.. By default it only contains the generated controls. You can see a Branch control with a property name equal Customer. This control also has the Contents property, which again may contain any aspx script code.

Branches and leafs

The Branch controls are those that represent data definitions that had children. The data definitions that collected text are represented by the Leaf controls. The Leaf controls do not contain the Contents property. Instead, they may contain the ValueHandler element, which provides a communication between the MForm tree and a control that should actually collect the data.

Value handlers

By default all data is collected using TextBoxes, so the default value handler is the TextBoxValueHandler. The MForm framework provides value handlers for the generic .NET form webcontrols. If you want to use your own custom control with the MForm framework, you have to implement a value handler for this control.

The example of a different value handler is in a Leaf called Sex. The data definitions allowed only two values in this element: Male or Female. This has been ported to the form as a listbox control, that has to corresponding list items. If you prefer using i.e. the RadioButtonList instead of the ListBox, you can change the value handler to a RadioButtonListValuehandler, and its inner control to RadioButtonList.

Data type

Do you remember that our customer data definition had a BirthDate field, which was of type date. You can now see that the BirthDate leaf has a property DataType of value Date. This asserts that this field data must be provided in a valid date format.

Additions

Another definition that may be found inside the Leaf control is the Additions. Additions may contain a list of controls that inherit from the Addition control. The Addition control adds some logic to the MForm control, it can for example add a data constraint. The Addition controls that can be found directly after generation are data restrictions taken from XML schema. So if in our data contract the PostCode field had to match the pattern: 2 digits - 3 digits, we will a Restriction addition of restriction type Pattern and value \d{2}-\d{3} in a leaf called PostCode.

The Ordinal property

One last thing that is worth explaining is the Ordinal property.

The MForm webcontrols were designed to maximally facilitate the creation of web forms, but still to give the possibility of editing this form after generation. One can freely modify a once generated form, only remembering that the generated MForm children cannot be moved outside their parents.

So inside the Contents property it is allowed to:

  • add some html code or controls;
  • place MForm child control inside these added controls (e.g. all Leaf controls can be placed inside a table);
  • extract a part of a generated code and place it in an external user control, and add a reference to this control inside the Contents;
  • change the order of MForm siblings;

When creating an output xml from the tree of MForm controls, two factors are taken into account: 

  1. the hierarchy of the XML nodes - if MForm children are not moved outside their parents, the MForm hierarchy remains intact, so the hierarchy of XML nodes will also be valid;
  2. the order of XML nodes - because the order of controls can be changed, the Ordinal property is generated. When the output xml is rendered, the MForm controls are sorted by the Ordinal property value, which asserts, that the order of XML nodes is valid

If you do not care about the order of XML nodes (i.e. deserializing xml to objects using the .NET XmlSerializer class in most of the cases totally ignores the order of XML nodes), you can turn off generation of the Ordinal property in the generator under the options tab. 

An Introduction to the MForm Web Controls

8. Run the page  

Add an asp button to the page. 

Then set it as the start page and run the project. 

You will see that the form works and is validated. You can also see that the data is persisted during postbacks. 

If you get the OutputXml property of the Root control after the form was filled in, you will get an Xml document that conforms to the Xml schema from which we have generated the form.  This is much more convenient than taking care of each of the fields separately.

What's next? 

I will try to post some more articles concerning the MForm webcontrols. The project is still in alpha stage but we are very close to our first stable release. Until that time, you are welcome to familiarize yourself with the MForm webcontrols using the BM.Tools.WebControls.MForm.Example project. We will be most grateful for submitting bugs and comments at mform.codeplex.com. You can also see the example project at mform.org.



About the Author

Michal Wozniak

Michal Wozniak is an IT specialist at Bank Millennium, Poland. He is responsible for designing components that facilitate the creation of web systems with large data processing. He is interested in the areas of data contracts (XML schema, RDF Schema, OWL), web controls, Ajax, workflows.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

  • Learn How A Global Entertainment Company Saw a 448% ROI Every business today uses software to manage systems, deliver products, and empower employees to do their jobs. But software inevitably breaks, and when it does, businesses lose money -- in the form of dissatisfied customers, missed SLAs or lost productivity. PagerDuty, an operations performance platform, solves this problem by helping operations engineers and developers more effectively manage and resolve incidents across a company's global operations. …

Most Popular Programming Stories

More for Developers

RSS Feeds