AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1680&pId=-1
Building ASP.NET Web Server Controls using XML and XSLT
page
by Ehsanul Haque
Feedback
Average Rating: 
Views (Total / Last 10 Days): 55784/ 107

Introduction

XML has a vast use in web application because of column flexibility and platform independence ability. Because of column flexibility, programmers can increase or decrease a column anytime without changing much code (like different types of address such as home address, office address, etc). On the other hand, XSLT can traverse XML faster which can operate with lots of XML data quickly and can give the application a dynamic layout without touching any single line of code from the code-behind. So the combination of XML and XSLT can show its own strength and beauty which can boost up our application.

Requirements

I am assuming that the reader of this article have basic knowledge in XML, XSLT and ASP.NET.

Background

A few days ago, my boss requested me to work with a huge amount of employees' data which will become a handy web service in XML Format. We can easily sort out the problem in the traditional way, but the problem is he was very concerned with performance. Also, the client asked that the layout be dynamic. I will describe the most interesting part of our implementation where we will show and edit a list of employees' address.

Figure 1

 

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!!!).

Downloads
Summary

ASP.NET server controls are very powerful in web application without a doubt and by combining XML and XSLT with it, we can make web applications more powerful, easier, more efficient and re-usable. Also, by using XML and XSLT, the application UI script will look clean and will be easier to maintain. I hope this will be a great start for those who want to make their site more dynamic and structured. Enjoy!!


Product Spotlight
Product Spotlight 

©Copyright 1998-2018 ASPAlliance.com  |  Page Processed at 2018-11-14 9:19:05 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search