Building ASP.NET Web Server Controls using XML and XSLT
page 2 of 4
by Ehsanul Haque
Feedback
Average Rating: 
Views (Total / Last 10 Days): 37561/ 43

Procedure

We will go through the following steps to get the things done:

1.    Prepare XML document

2.    Prepare XSLT document to transform XML data

3.    Define server controls according to XML document and event handler

4.    Define validation control according to XML document

5.    Parse Controls and Define Event Handler

Prepare XML Document

Our sample XML Document will look like:

Listing 1

<root>
  <Employee Id="1">
    <Address Caption="Address">
      <Home Caption="Home 1">
        <Street Caption="Street 1" Type="Text">Road# 27, House# 13, Banani</Street>
        <Street Caption="Street 2" Type="Text"></Street>
        <City Caption="City" Type="Text" Required="yes">Dhaka</City>
        <Zip Caption="Zip" Type="Text">1213</Zip>
        <Country Caption="Country" Type="CountryDDL">BD</Country>
      </Home>
      <Home Caption="Home 2">
        <Street Caption="Street 1" Type="Text">Sector- 10</Street>
        <Street Caption="Street 2" Type="Text">Uttara</Street>
        <Street Caption="Street 3" Type="Text">
        </Street>
        <City Caption="City" Type="Text" Required="yes">Dhaka</City>
        <Zip Caption="Zip" Type="Text">1230</Zip>
        <Country Caption="Country" Type="CountryDDL">BD</Country>
      </Home>
    </Address>
  </Employee>
  <Employee Id="2">
    <Address Caption="Address">
      <Home Caption="Home 1">
        <Street Caption="Street 1" Type="Text">J-13, Road 27</Street>
        <Street Caption="Street 2" Type="Text">Banani</Street>
        <City Caption="City" Type="Text">Dhaka</City>
        <Zip Caption="Zip" Type="Text">1213</Zip>
        <Country Caption="Country" Type="CountryDDL">BD</Country>
      </Home>
      <Home Caption="Home 2">
        <Street Caption="Street 1" Type="Text">Michigan Avenue</Street>
        <Street Caption="Street 2" Type="Text">Suite 2800</Street>
        <Street Caption="Street 3" Type="Text"></Street>
        <City Caption="City" Type="Text">Chicago</City>
        <Zip Caption="Zip" Type="Text">60601</Zip>
        <Country Caption="Country" Type="CountryDDL">USA</Country>
      </Home>
    </Address>
  </Employee>
</root>  

This is a very ordinary XML to keep track of employee addresses. But if we look carefully then we will find that the XML document contains some extra information in each node, which is not related with addresses. I am explaining them one by one below.

Caption - will be used to show the caption of the value

Type - will explain the ASP.NET Server control type

Required - will saiy explicitly that the field is a required field or non required field

We can also keep other information to make the process easier in XSLT side, if needed.

Prepare XSLT document to transform XML data

To get specific Employee’s information among multiple employees' data, we have to pass the employee's id to the XSLT document. We can do that by simply passing the XSLT argument list to the XslCompiledTransform. The Transform function looks like the following.

Listing 2

//create argument list
XsltArgumentList xslArg = new XsltArgumentList();
xslArg.AddParam("employeeId", "", CurrentEmployeeId);
 
//load the data
XPathDocument xdoc = new XPathDocument(Server.MapPath("Address.xml"));
//load Xslt
XslCompiledTransform transform = new XslCompiledTransform();
transform.Load(Server.MapPath("DynamicControls.xslt"));
StringWriter sw = new StringWriter();
//transform it
transform.Transform(xdoc, xslArg, sw);

The parameter can be received from XSLT document using xsl:param which can be then used to get the specific employee's address.

Listing 3

<xsl:param name="employeeId"/>
<xsl:for-each select="Employee">
   <xsl:if test="@Id = $employeeId">
      <table width="100%" border="0" cellspacing="0" 
        cellpadding="0" bgcolor="#FFFFFF">
        <tbody>
          <tr>
            <td>
              <xsl:apply-templates select="Address">
              </xsl:apply-templates>

Now after getting the specific Employee’s address, we have to define the server controls for each child node (like street, city, country) of address node that has been explicitly stated in XML document. Also, we have to declare the required field validator for the required fields and captions for the caption of the fields. We can also embed the validation logic (such as regular expression) in XML which can be used here to check valid data.

Listing 4

<xsl:for-each select="child::*">
<xsl:variable name="rowindex" select="position()"></xsl:variable>
      <td align="left" valign="top">
            <table cellpadding="4">
                  <tr>
                    <td colspan="4" height="28" align="left" valign="top">
                       <strong>
                          <xsl:value-of select="@Caption"/>
                        </strong>
                      </td>
                    </tr>
 
                    <xsl:for-each select="child::*">
                      <xsl:variable name="varType" select="@Type"></xsl:variable>
                      <xsl:variable name="isRequired" 
                        select="@Required"></xsl:variable>
                      <xsl:variable name="varId" 
select="translate(concat(concat(concat(@Caption,'_'),$rowindex),position()),' ','_')">
                      </xsl:variable>
                      <xsl:variable name="up" 
                        select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
                      <xsl:variable name="lo" 
                        select="'abcdefghijklmnopqrstuvwxyz'"/>
                      <tr>
                        <td height="28" align="left" valign="top">
                          <xsl:value-of select="@Caption"/>
                        </td>
                        <td colspan="3" align="left" valign="top" height="28">
                          <xsl:choose>
                            <xsl:when 
                              test="translate($varType,$up,$lo)='countryddl'">
                              <asp:DropDownList
                                id="{concat('ddlCountry',$rowindex)}" 
                                runat="server" DataTextField="Text" 
                                DataValueField="Value">
                                <asp:ListItem value="{.}">
                                  <xsl:value-of select="."/>
                                </asp:ListItem>
                              </asp:DropDownList>
                            </xsl:when>
                            <xsl:otherwise>
                              <asp:TextBox ID="{$varId}" runat="server" Text="{.}" 
                                width="205px" ></asp:TextBox>
                              <xsl:if test="translate($isRequired,$up,$lo)='yes'">
                                <asp:RequiredFieldValidator 
                                  ErrorMessage=" Required Field" runat="server" 
                                  ControlToValidate="{$varId}" />
                              </xsl:if>
                            </xsl:otherwise>
                          </xsl:choose>
                        </td>
                      </tr>
                  </xsl:for-each>
            </table>
</td>
</xsl:for-each>

In the above code we have used XML node's position to ensure unique id for each server controls. Two variables, $up and $lo, have been used to convert all the letters to lowercase since XLST does not have any built-in functions to do this.

Parse Controls and Define Event Handler

XSLT will produce a plain XML string from which we have to parse to ASP.Net server controls. We can accomplish the task by simply calling Page.ParseControl function. Also, to get a reference of newly created controls, we have to call the FindControl method. As soon as we get the reference of the control, we can set the event handler for that control. One thing is very important to know, the event handler adding process should be after parsing the control. Before parsing, the control will not be available which can throw an exception. Also, Parsing should be in Page_Init event so that the controls will be found in the rest of the ASP.NET life cycle.

Listing 5

protected void Page_Init(object sender, EventArgs e)
{
    if (Context.Request.QueryString["id"] != null)
    {
        ParseControls();
        BindInfo();
    }
}
 
private void ParseControls()
{
    int EmployeeId = Convert.ToInt32(Context.Request.QueryString["id"]);
    CurrentEmployeeId = EmployeeId;
 
    //create argument list
    XsltArgumentList xslArg = new XsltArgumentList();
    xslArg.AddParam("employeeId", "", CurrentEmployeeId);
 
    //load the data
    XPathDocument xdoc = new XPathDocument(Server.MapPath("Address.xml"));
    //load Xslt
    XslCompiledTransform transform = new XslCompiledTransform();
    transform.Load(Server.MapPath("DynamicControls.xslt"));
    StringWriter sw = new StringWriter();
    //transform it
    transform.Transform(xdoc, xslArg, sw);
    string result = sw.ToString();
 
    //remove namespace
    result = result.Replace("xmlns:asp=\"remove\"""");
    //parse control
    Control ctrl = Page.ParseControl(result);
    phEmployeeAddress.Controls.Add(ctrl);
        
}
private void BindInfo()
{
    lblMessage.Text = "";
        
    //find dropdown control and update datasource
    try
    {
        DropDownList ddlCountry1 = 
          phEmployeeAddress.FindControl("ddlCountry1") as DropDownList;
        string selectedValue1 = ddlCountry1.Items[0].Value.Trim();
        ddlCountry1.DataSource = GetCountryList();
        ddlCountry1.DataBind();
 
        DropDownList ddlCountry2 = 
          phEmployeeAddress.FindControl("ddlCountry2") as DropDownList;
        string selectedValue2 = ddlCountry2.Items[0].Value.Trim();
        ddlCountry2.DataSource = GetCountryList();
        ddlCountry2.DataBind();
 
        //select the selected value
        ddlCountry1.SelectedValue = selectedValue1;
        ddlCountry2.SelectedValue = selectedValue2;
    }
    catch 
    { 
        //ex
        throw;
    }
    //find control to add event handler
    /*===============================================================
    Note: Event handler adding process should be after parsing 
    * the controls since controls will be available after parsing
    =================================================================*/
    Button btnSaveAddress =
      (Button)phEmployeeAddress.FindControl("btnSaveAddress");
    btnSaveAddress.Click += new EventHandler(btnSaveAddress_Click);
 
    
    DropDownList ddlEmployee =
      (DropDownList)phEmployeeAddress.FindControl("ddlEmployee");
    ddlEmployee.SelectedValue = CurrentEmployeeId.ToString();
    ddlEmployee.SelectedIndexChanged+=
      new EventHandler(ddlEmployee_SelectedIndexChanged);
} 

We have almost covered everything. The most interesting thing is that XSLT will make our ASPX page very simple and clear looking which will increase the maintainability. Here is the full ASPX source.

Listing 6

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="edit-address.aspx.cs" 
Inherits="edit_address"%>
<span style='background:yellow'> </span>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Dynamic ASP.net Controls Using Xslt</title>
</head>
<body>
    <form id="form1" runat="server">
    <div style="float: right; background-color: Yellow">
        <asp:Label ID="lblMessage" runat="server" Text=""></asp:Label>
    </div>
    <div>
         <asp:PlaceHolder ID="phEmployeeAddress" runat="server"></asp:PlaceHolder>
    </div>
    </form>
</body>
</html>  

All the server controls will be extracted to the place holder.

Note: To get the best performance, I will recommend that you store the XML and XSLT in database side and use a caching technique to retrieve the XSLT template. This way the application will be boosted up in such a way for the large amount of data (which will be unbelievable!!!).


View Entire Article

User Comments

Title: v   
Name: m
Date: 2012-10-20 11:34:01 AM
Comment:
n
Title: Ms   
Name: Poornima Thirumoorthy
Date: 2012-08-28 3:28:55 AM
Comment:
Thank you for your response. Looking forward for the URL.
Title: Missing URL of Sample Code   
Name: Ehsanul Haque
Date: 2012-08-22 4:13:27 PM
Comment:
Hello Everybody,
I do apologize that you are not able to download the sample code from here. It seems aspalliance is not maintaining the file hosting server. I tried to communicate with them but they didn't replied. I am trying to host the code in different server. I will post the new URL as a comment once it is available.
Title: Ms   
Name: Poornima Thirumoorthy
Date: 2012-08-09 12:42:01 PM
Comment:
very information article. But not able to download the code. can you send it to me.
Title: fine   
Name: Anwar
Date: 2012-08-01 2:22:10 AM
Comment:
Very Helpful
Title: You Saved my day   
Name: Mansoor
Date: 2010-04-23 10:33:58 AM
Comment:
Hey thanks a lot.Nice article and great explanation.This is similar to what i am looking for and i curse my luck that i found this one so lately.But anyways this is the best one i found and thanks once again.
Title: RE: Great help   
Name: Ehsan
Date: 2009-07-26 11:31:54 AM
Comment:
Hi docere,
Glad to know that this code helped you. However, if you look over the "edit-address.aspx" then you will find that I have shown a way how to work with event handler and set it from XSLT. If you work on debug mode, you will see that event is firing and working exactly as we expect from a traditional event handler.

Thanks!
Title: Great help   
Name: docere
Date: 2009-07-26 7:11:15 AM
Comment:
that article covered exactly what i was trying to do! thank you very much, it was a great help!

Still i have the problem that the generated event handler is never fired - it just postback, my generated form is gone and the eventhandler operation is never reached. that is driving me to the borders of sanity, seriously...

anyway, good job
Title: Great Job   
Name: Xiaohong
Date: 2008-09-17 4:34:23 PM
Comment:
big helps, thank you!
Title: Designer   
Name: Ehsan
Date: 2008-09-17 2:58:10 PM
Comment:
Hi,
This is a very good question. Though VS don't have XSLT design view support, change in xslt is very easy rather than changing design of page dynamically in traditional way. In this technique total design and data will be separate from each other. In the xml, we are just keeping the data as well as some valuable information like validation, data type, caption, etc. But the design template is totally separate from the data template to design independently. And Being template based, XSLT is more resilient to changes in documents than low level DOM and SAX. Also By separating data (XML document) from the presentation (XSLT), it is very easy to change the output format in any time easily without touching the code-behind. In case of repeatating design (like employee paystub design), the XSLT template will be extremely small sized by repeatating calling as it works template based. If designer is quite smarter then they can make the design more dynamic than any other way using this simple technique.

Thanks
Title: Designer   
Name: redsquare
Date: 2008-09-17 9:35:05 AM
Comment:
With all that markup and inline styling can I ask how a web designer is supposed to design and edit the content if the developer has bundled it into xml files??
Title: Nice Article   
Name: Kamran Shahid
Date: 2008-09-16 1:46:55 AM
Comment:
Very Nice One Ehsan.
Title: Congratulations   
Name: Ahsanul Haque
Date: 2008-08-03 7:31:28 AM
Comment:
I am proud, infact very proud watching this. Keep it up.
congratulations.
Title: awesome   
Name: Mamun
Date: 2008-08-02 10:40:20 AM
Comment:
Great job ehsan vai. Keep up these good works.
Title: good job   
Name: Kamrul Hassan
Date: 2008-07-31 3:01:50 PM
Comment:
i'm familiar with xml data binding in the traditional way. But after reading this article, hopefully I am going to use this technique for the upcoming development in my software career life :D.
Well done !!! Great work.

Thanks you for your nice well commented code and obviously the clear explanation.
Title: nice   
Name: shahin mahmud
Date: 2008-07-31 3:00:24 PM
Comment:
Nice job , go ahead
Title: Good One   
Name: Ferdous Hassan
Date: 2008-07-31 2:59:43 PM
Comment:
Hi Ehsanul,
I am a software engineer of a company. Recently I am working with a good amount data and I was looking for a XML solution. And this is exactly what I need to start. Thanks for sharing a good idea.
Title: ExCeLlEnTo   
Name: Shahid Khan
Date: 2008-07-31 10:50:00 AM
Comment:
ExCeLlEnTo.....
gReAt WoRk buddy !!!
Title: Excellent   
Name: Khabir
Date: 2008-07-31 10:45:31 AM
Comment:
Good job. keep it up.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-19 6:22:48 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search