5,693,062 members and growing! (14,036 online)
Email Password   helpLost your password?
Web Development » ASP.NET » General     Intermediate

How to use ASP.NET controls inside XSLTs

By Bcoelho2000

Shows you how to generate ASP.NET controls from XSLT
XML, HTML, C# 1.0, C# 2.0, C#, Windows, .NET, .NET 1.0, .NET 1.1, .NET 2.0, ASP.NET, Visual Studio, WebForms, Dev

Posted: 10 Dec 2006
Updated: 15 Feb 2007
Views: 22,573
Bookmarked: 28 times
Announcements
Loading...



Search    
Advanced Search
Sitemap
8 votes for this Article.
Popularity: 3.86 Rating: 4.27 out of 5
0 votes, 0.0%
1
1 vote, 12.5%
2
0 votes, 0.0%
3
1 vote, 12.5%
4
6 votes, 75.0%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

Introduction

One of the major disadvantages of using XSLTs in your code is the fact that you can't use ASP.NET controls inside the XSLT code. Well... at least until now!

Why can't I use ASP.NET controls inside a XSLT?

You can't use ASP.NET controls inside a XSLT because when the XSLT/XML transformation is made, the page that is dynamically created and contains all the ASP.NET controls is already created. Remenber that ASP.NET pages are dynamically compiled on demand when first required in the context of a Web application.

A possible solution

Now suppose you've some ASP.NET controls inside your XSLT that you want to use.

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:asp="remove"
    exclude-result-prefixes="msxsl vt">
    
    <xsl:output method="html" version="4.0"/>

    <xsl:template match="/">
        <div>
            <asp:TextBox runat="server" ID="NomeCompleto"><xsl:value-of select="c"/></asp:TextBox>
        </div>
    </xsl:template>
    
</xsl:stylesheet>

Note, that I've declared the asp namespace so the XSLT processor knows that "new" namespace.

You have to execute the XSLT/XML transformation, store the html result in a string and remove the string xmlns:asp="remove" from it.

The next thing you need to do is execute the ParseControl method passing the string with the html of the transformation as a parameter. This method returns a reference to a Control. In fact, this control has all the ASP.NET controls that were inside your XSLT.

Next, you have to add that collection of controls to your page's control collection, or if you want, to a placeholder control collection.

Remenber that all this code must be executed on the Init event of the page because on the Init event the viewstate isn't already restored and server-side events aren't alredy fired. To get a reference to one of the newly created Controls you have to call the FindControl method... for each control that you want to use.

private void Page_Init(object sender, System.EventArgs e)
{     
    string xml = // string com o xml

    string html = XMLFunctions.GetXSLXMLFile(Server.MapPath("../XSL/A.xslt"), xml);  // Do the transform...


    html = html.Replace("xmlns:asp=\"remove\"", String.Empty);//Retirar o namespace

    Control ctrl = ParseControl(html);//Get the control collection

    OutPutPlace.Controls.Add(ctrl);// OutPutPlace is a placeholder

 
    // Register some events

    Button okBtn = this.Page.FindControl("OK") as Button; // Find a Button that was inside the XSLT

    okBtn.Click += new System.EventHandler(this.OK_Click);// Registar o evento

}

Disadvantages

The fact that you've to call the ParseControl method to get the control collection and the calls to FindControl to get a reference to a control.

The Challenge

The challenge is to make the XSLT/XML transformations, if any, before the ASP.NET page, for the current request, is created.

The solution

To meet the challenge we have to run some code inside the HTTP Pipeline before the PageHandlerFactory runs. The page handler factory is responsible for either finding the assembly that contains the page class or dynamically creating an ad hoc assembly.

I use a HttpModule to capture all requests to my ASP.NET pages. If the requested page has a special element that I've defined ("AspXsl:Out") then the module must make a XSLT/XML transformation.

Obviously, the result of the transformation can't be injected in the current page because then I would lose the information that I need to make the transformation itself, such as the xsl file and xml file or method to invoke to get the xml.

So I create a new page with the same name as the page that was requested and save it in a temporary directory that I've called DinTemp. In this new page, I inject the html of the XSLT/XML transformations.

When all transformations are done the Http Pipeline work is redirected to the newly created page.

To improve performance, I save the path of the newly created file in the Cache with the following FileDependecies:

  • The requested page itself
  • All the XSLT files that are needed to create the final ASP.NET page
This way any changes made to one of the listed files will cause my module to recreate the temporary ASP.NET file with the result of all XSLT/XML transformations.

Time to see some code.

private void app_BeginRequest(object sender, EventArgs e)
{
    HttpApplication app = (HttpApplication)sender;
    HttpRequest request = app.Request;
    HttpContext context = app.Context;

    if(request.Url.AbsoluteUri.IndexOf("DinTemp")==-1)
    {
        string siteName = GetSiteName(request.Url);
        string pathPageName = GetRequestedPagePath(request.Url, siteName);
    
        if(context.Cache[pathPageName]==null)
        {
            // If the cache entry is invalid, then we must rewrite the new ASP.NET page

            #region Do work
            string pageName = GetRequestedPageName(request.Url);
            string dinPagePath = context.Server.MapPath(siteName+@"DinTemp\")+pathPageName;
            // Get the final html from the requested ASP.NET page.

            // The ProcessHtml will save a new entry in the cache.

            string html = ProcessHtml(siteName, pageName, context.Server.MapPath( pageName ), context);
            // Write the new ASP.NET file

            using (StreamWriter sw = new StreamWriter(dinPagePath)) 
                sw.Write(html);
            #endregion
        }
        RedirectToGeneratedPage(request, context, @"\DinTemp\"+pathPageName);
    }
}

Great! How can I use this module in my ASP.NET pages?

The new element that this module uses is AspXsl:Out and has the following attributes:

  • Xsl: The path to the xsl file
  • XmlFile: The path to the xml file
  • XmlMethod: The method of the page to call to get the xml

You can have a ASP.NET page called C.aspx with this code:

<%@ Page Trace="true" language="c#" Codebehind="C.aspx.cs" AutoEventWireup="false" Inherits="AspXsl.C" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
    <HEAD>
        <title>C</title>
        <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
        <meta name="CODE_LANGUAGE" Content="C#">
        <meta name="vs_defaultClientScript" content="JavaScript">
        <meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5">
    </HEAD>
    <body>
        <form id="Form1" method="post" runat="server">
            <div>
                <AspXsl:Out id="target" Xsl="Xsl/C.xslt" XmlMethod="GetXml" />
            </div>
            <div>
                <asp:Button Runat="server" ID="ok" Text="Post it!" />
            </div>
            <div>
                <asp:Label Runat="server" ID="Msg" />
            </div>
        </form>
    </body>
</HTML>
Notice that AspXsl:Out element again? I'm saying to my HttpModule that I have a XSLT file in "Xsl/C.xslt" and I want to transform the xml that the GetXml method returns.

Let's look at the code behind:

namespace AspXsl
{
    /// <summary>

    /// Summary description for C.

    /// </summary>

    public class C : System.Web.UI.Page
    {
        protected System.Web.UI.WebControls.Button ok;
        protected System.Web.UI.WebControls.Label Msg;
        
        protected TextBox NomeCompleto;// Inside the XSLT


        private void Page_Load(object sender, System.EventArgs e)
        {
        }

        public static string GetXml(HttpRequest request){return "<c>Xml is fun</c>";}

        private void ok_Click(object sender, System.EventArgs e)
        {
            Msg.Text = "Olá "+NomeCompleto.Text;
        }
    }
}
See? The code looks like the code that you have in your other ASP.NET pages! How great is that?

Conclusion

I hope that with this HttpModule you can get the best of the two worlds: XSLT and ASP.NET.

You have the code for the HttpModule and a demo web app as well to try!

As always, tell what you think!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Bcoelho2000


Bruno Coelho is a Software Engineer from Portugal.

He's been working with .NET since version 1.1 to 3.5, using the hard won skills at ISEL, where he has got a degree in Informatic Engineering, to design critical applications using the best practices and design patterns in the industry.

He's current interests are: Performance & Best practices and Design patterns.
Occupation: Software Developer
Company: Advantis
Location: Portugal Portugal

Other popular ASP.NET articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 9 of 9 (Total in Forum: 9) (Refresh)FirstPrevNext
GeneralThere is another simple solution!!!!memberEnigmatic5:16 31 Jan '08  
GeneralRe: There is another simple solution!!!!memberBcoelho200012:59 1 Apr '08  
GeneralHelp me to launch the project!memberEnigmatic1:29 31 Jan '08  
GeneralRe: Help me to launch the project!memberBcoelho200012:53 1 Apr '08  
GeneralFlexibile solution! :)memberserghei423:36 28 Dec '07  
Generalclientside validationsmembersk840520:27 4 Sep '07  
Generalone suggestionmemberIsmail Mayat0:46 14 Dec '06  
GeneralVery good strategy.memberilovthecov6:23 12 Dec '06  
GeneralRe: Very good strategy.memberBcoelho23:11 12 Dec '06  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 15 Feb 2007
Editor:
Copyright 2006 by Bcoelho2000
Everything else Copyright © CodeProject, 1999-2008
Web16 | Advertise on the Code Project