Introduction
Sometimes you need to create dynamic pages. You may allow users to build their own applications by selecting the fields they need. In this article, I will show how to build dynamic applications by using ASP.NET and XML. In this example, I use XML for meta-programming or generative programming. I use XML with an XSLT to generate ASP.NET PRE, utilizing intrinsic ASP.NET parsing methods with the XSLT output. Steps that we need to do:
- Create XML file (schema of our page).
- Create an XSLT style to transform XML into ASP.NET PRE.
- Transform XML, and create server controls defined by the generated ASP.NET PRE at runtime.
- Insert the instantiated controls into the page's control collection.
- Handle postback events from server controls.
First, what we need to come up with is a good structure of our page in a XML file. A basic page structure is defined below. The example structure consists of field elements, with attributes, properties and listitems.
<FORM>
<PAGES>
<PAGE title="Page Title" id="page_1">
<FIELDS>
-->
<FIELD type="TextBox" label="Last Name:" required="true">
<PROPERTIES>
<PROPERTY name="ID">LAST_NAME</PROPERTY>
</PROPERTIES>
</FIELD>
-->
<FIELD type="TextBox" label="Description:">
<PROPERTIES>
<PROPERTY name="ID">DESCRIPTION</PROPERTY>
<PROPERTY name="TextMode">MultiLine</PROPERTY>
<PROPERTY name="Cols">30</PROPERTY>
<PROPERTY name="Rows">2</PROPERTY>
</PROPERTIES>
</FIELD>
-->
<FIELD type="TextBox" label="Prefilled Contrl:">
<PROPERTIES>
<PROPERTY name="ID">PREFILLED_CONTROL</PROPERTY>
<PROPERTY name="Text">Some Text</PROPERTY>
</PROPERTIES>
</FIELD>
-->
<FIELD type="TextBox" label="Start Date:"
required="true" validation="Date">
<PROPERTIES>
<PROPERTY name="ID">START_DATE</PROPERTY>
</PROPERTIES>
</FIELD>
-->
<FIELD type="DropDownList" label="Title:">
<PROPERTIES>
<PROPERTY name="ID">TITLE</PROPERTY>
</PROPERTIES>
<LISTITEMS>
<LISTITEM value="">Select One</LISTITEM>
<LISTITEM value="1">Architector</LISTITEM>
<LISTITEM value="2">Sr. Developer</LISTITEM>
<LISTITEM value="3">Programmer</LISTITEM>
<LISTITEM value="4">Web Designer</LISTITEM>
</LISTITEMS>
</FIELD>
-->
<FIELD type="RadioButtonList" label="Are you US citizen?">
<PROPERTIES>
<PROPERTY name="ID">IS_US_CITIZEN</PROPERTY>
<PROPERTY name="RepeatColumns">1</PROPERTY>
<PROPERTY name="RepeatDirection">Vertical</PROPERTY>
<PROPERTY name="RepeatLayout">Table</PROPERTY>
<PROPERTY name="TextAlign">Right</PROPERTY>
</PROPERTIES>
<LISTITEMS>
<LISTITEM value="1">Yes</LISTITEM>
<LISTITEM value="0">No</LISTITEM>
</LISTITEMS>
</FIELD>
-->
<FIELD type="CheckBoxList" label="Languages:">
<PROPERTIES>
<PROPERTY name="ID">LANGUAGES</PROPERTY>
<PROPERTY name="RepeatColumns">1</PROPERTY>
<PROPERTY name="RepeatDirection">Vertical</PROPERTY>
<PROPERTY name="RepeatLayout">Table</PROPERTY>
<PROPERTY name="TextAlign">Right</PROPERTY>
</PROPERTIES>
<LISTITEMS>
<LISTITEM value="C#">C#</LISTITEM>
<LISTITEM value="Java">Java</LISTITEM>
<LISTITEM value="VB">Visual Basic</LISTITEM>
</LISTITEMS>
</FIELD>
-->
<FIELD type="HyperLink">
<PROPERTIES>
<PROPERTY name="ID">LINK</PROPERTY>
<PROPERTY name="NavigateUrl">javascript:void(alert('Hello ooo'));
</PROPERTY>
<PROPERTY name="Text">Say Hello</PROPERTY>
</PROPERTIES>
</FIELD>
<FIELD type="html" src="file path"/>
-->
</FIELDS>
</PAGE>
...
</PAGES>
</FORM>
Next step is to create XSLT style to transform our XML schema The XSLT iterates through each field, first outputting a label for the field as plain text. The stylesheet also checks if the field is required, and adds a RequiredFieldValidator
if needed. The stylesheet then creates a Web Control (it could be any valid web control such as TextBox
, RadioButtonList
, DropDownList
, etc.). ListItem
s are created for each of the ListControls.
="1.0" ="UTF-8"
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:asp="remove">
<xsl:output method="xml" indent="yes" encoding="utf-8" omit-xml-declaration="yes">
</xsl:output>
<xsl:template match="/">
-->
<xsl:param name="pageid"/>
-->
<table cellpadding="0" cellspacing="5">
-->
<tr>
<td colspan="3" align="center" style="font-size:25px">
<xsl:value-of select="FORM/PAGES/PAGE[@id=$pageid]/@title" />
</td>
</tr>
<tr><td colspan="3" style="height:20px"></td></tr>
-->
<xsl:for-each select="FORM/PAGES/PAGE[@id=$pageid]/FIELDS/FIELD">
-->
<xsl:element name="tr">
<xsl:attribute name="id">
TR_<xsl:value-of select="PROPERTIES/PROPERTY[@name='ID']"></xsl:value-of>
</xsl:attribute>
-->
<xsl:if test="@display='none'">
<xsl:attribute name="style">display:none;</xsl:attribute>
</xsl:if>
<xsl:choose>
-->
<xsl:when test="@type='HTML'">
<td colspan="3">
-->
</td>
</xsl:when>
-->
<xsl:otherwise>
-->
<td valign="top">
<xsl:value-of select="@label" />
</td>
-->
<td>
-->
<xsl:element name="asp:{@type}">
<xsl:attribute name="runat">server</xsl:attribute>
<xsl:for-each select="./PROPERTIES/PROPERTY">
<xsl:attribute name="{@name}">
<xsl:value-of select="current()"></xsl:value-of>
</xsl:attribute>
</xsl:for-each>
<xsl:for-each select="./LISTITEMS/LISTITEM">
<asp:ListItem value="{@value}">
<xsl:value-of select="current()"></xsl:value-of>
</asp:ListItem>
</xsl:for-each>
</xsl:element>
</td>
-->
<td>
<xsl:if test="@required='true'">
<asp:RequiredFieldValidator ErrorMessage="Required" runat="server"
ControlToValidate="{PROPERTIES/PROPERTY[@name='ID']}" />
</xsl:if>
<xsl:if test="@validation='Date'">
<asp:CompareValidator ErrorMessage="Dates Only" runat="server"
Operator="DataTypeCheck" Type="Date"
ControlToValidate="{PROPERTIES/PROPERTY[@name='ID']}" />
</xsl:if>
<xsl:if test="@validation='Number'">
<asp:CompareValidator ErrorMessage="Numbers Only" runat="server"
Operator="DataTypeCheck" Type="Integer"
ControlToValidate="{PROPERTIES/PROPERTY[@name='ID']}" />
</xsl:if>
<xsl:if test="@validation='Currency'">
<asp:CompareValidator ErrorMessage="Currency Only" runat="server"
Operator="DataTypeCheck" Type="Currency"
ControlToValidate="{PROPERTIES/PROPERTY[@name='ID']}" />
</xsl:if>
</td>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:for-each>
</table>
</xsl:template>
</xsl:stylesheet>
Our XSLT file has a prefix (xmlns:asp="remove"
) defined for the XSL namespace. This is used to generate plain HTML. Now we are ready to write our Page transformer.
...
private readonly string XslFile = @"...\default.xslt";
private readonly string XmlFile = @"...\default.config";
...
private void Page_Load(object sender, System.EventArgs e)
{
string pageId = "page_1";
if (!Page.IsPostBack)
{
XmlDocument xdoc = new XmlDocument();
xdoc.Load(XmlFile);
XslTransform xsl = new XslTransform();
xsl.Load(XslFile);
XsltArgumentList xslarg = new XsltArgumentList();
xslarg.AddParam("pageid", "", pageId);
StringWriter sw = new StringWriter();
xsl.Transform(xdoc, xslarg, sw);
string result = sw.ToString().Replace("xmlns:asp=\"remove\"",
"").Replace("<","<").Replace(">",">");
sw.Close();
Control ctrl = Page.ParseControl(result);
Page.Controls.Add(ctrl);
}
}
private void BtnSave_Click(object sender, System.EventArgs e)
{
Response.Write (Request.Form["LAST_NAME"]);
}
Conclusion
In this article, we separate data from content to make a cleaner design and for better maintainability. When XML is combined with XSLT, ASP.NET server controls become even more powerful. This opens up numerous possibilities for dynamic and robust systems.