65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在 XSLT 中使用 ASP.NET 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.26/5 (9投票s)

2006年12月10日

CPOL

4分钟阅读

viewsIcon

84243

downloadIcon

1421

展示了如何从 XSLT 生成 ASP.NET 控件。

引言

在代码中使用 XSLT 的主要缺点之一是无法在 XSLT 代码中使用 ASP.NET 控件。嗯……至少现在不是了!

为什么我不能在 XSLT 中使用 ASP.NET 控件?

您不能在 XSLT 中使用 ASP.NET 控件,因为当 XSLT/XML 转换完成时,包含所有 ASP.NET 控件的动态创建的页面已经生成。请记住,ASP.NET 页面是在 Web 应用程序的上下文中首次需要时动态编译的。

一个可能的解决方案

现在,假设您的 XSLT 中有一些 ASP.NET 控件您想使用。

<?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>

请注意,我已经声明了 asp 命名空间,以便 XSLT 处理器知道“新”命名空间。

您必须执行 XSLT/XML 转换,将 HTML 结果存储在一个字符串中,然后从该字符串中删除 xmlns:asp="remove"

接下来您需要做的是执行 ParseControl 方法,将包含转换 HTML 的字符串作为参数传递。此方法返回一个控件的引用。实际上,这个控件包含了您 XSLT 中的所有 ASP.NET 控件。

接下来,您需要将这些控件集合添加到页面的控件集合中,或者如果您愿意,也可以添加到占位符控件集合中。

请记住,所有这些代码都必须在页面的 Init 事件中执行,因为在 Init 事件中,视图状态尚未恢复,服务器端事件尚未触发。要获取新创建的控件之一的引用,您必须调用 FindControl 方法……对于您想使用的每个控件。

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
}

缺点

您必须调用 ParseControl 方法来获取控件集合,以及调用 FindControl 来获取控件的引用。

挑战

挑战在于在为当前请求的 ASP.NET 页面创建之前,执行 XSLT/XML 转换(如果需要)。

解决方案

为了应对这一挑战,我们必须在 PageHandlerFactory 运行之前,在 HTTP 管道内运行一些代码。页面处理程序工厂负责查找包含页面类的程序集或动态创建临时程序集。

我使用一个 HttpModule 来捕获对我的 ASP.NET 页面的所有请求。如果请求的页面包含我定义的特殊元素(“AspXsl:Out”),那么该模块必须执行 XSLT/XML 转换。

显然,转换结果不能被注入到当前页面中,因为那样我将丢失进行转换本身所需的信息,例如 XSL 文件和 XML 文件或调用以获取 XML 的方法。

所以,我创建了一个与请求的页面同名的页面,并将其保存在一个我称之为 DinTemp 的临时目录中。在这个新页面中,我注入了 XSLT/XML 转换的 HTML。

当所有转换完成后,HTTP 管道工作将被重定向到新创建的页面。

为了提高性能,我使用以下 FileDependecies 将新创建文件的路径保存在缓存中

  • 请求的页面本身
  • 创建最终 ASP.NET 页面所需的所有 XSLT 文件

这样,对列出的任何文件进行的更改都会导致我的模块使用所有 XSLT/XML 转换的结果重新创建临时 ASP.NET 文件。

是时候看些代码了。

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);
    }
}

太棒了!我如何在我的 ASP.NET 页面中使用这个模块?

该模块使用的新元素是 AspXsl:Out,它具有以下属性

  • Xsl:XSL 文件的路径
  • XmlFile:XML 文件的路径
  • XmlMethod:要调用的页面的方法以获取 XML

您可以有一个名为 C.aspx 的 ASP.NET 页面,其中包含此代码

<%@ 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>

再次注意到 AspXsl:Out 元素?我正在告诉我的 HttpModule,我在“Xsl/C.xslt”中有一个 XSLT 文件,我想转换 GetXml 方法返回的 XML。

我们来看看代码隐藏文件

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;
        }
    }
}

看到了吗?代码看起来就像您在其他 ASP.NET 页面中的代码!这难道不是很棒吗?

结论

我希望通过这个 HttpModule,您可以获得两个世界的优势:XSLT 和 ASP.NET。我已经为您提供了 HttpModule 的代码和一个演示 Web 应用程序供您尝试!一如既往,请告诉我您的想法!

© . All rights reserved.