独上高楼网站
  •    你所在位置:首页 VS.netXMLXML经验〉XSLT 和 ASP.NET 实用性对比 (Extreme XML)
  • XSLT 和 ASP.NET 实用性对比 (Extreme XML)
  • 作者:佚名  文章来源:http://msdn2.microsoft.com/zh-cn/default.aspx  发布日期:2007-07-07  浏览次数:520
  • 打印这篇文章
  • 下载 Xml02192001.exe

    简介

    人们正在使用 XML 来管理大型复杂网站的发布过程。示例包括 interview with Mike Moore(介绍 www.microsoft.com 如何使用 XML 来管理其复杂的需要)和 Streamlining Your Web Site Using XML(深入概述如 Dell 这样的公司如何使用 XML 来简化它们的整个发布过程)。

    许多客户会问我这些问题:他们是否应当放弃 XML/XSL 而转向编写大量 C# ASP.NET 代码?如果他们已经在 XSLT 方面进行了大量投资,ASP.NET 是否为它们提供任何价值?是否存在一种折衷方案使他们能够同时获得 XSLT ASP.NET 的最佳功能?如果存在,每种技术有哪些优缺点,人们应当在什么情况下使用它们?

    我将深入剖析一个具体示例,以便可以对 XSLT ASP.NET 进行比较和对照,这个示例就是 MSDN TOCMSDN 发现 XML 最适于管理它的大型目录 (TOC)。此 TOC 的内容来自公司内的数百个组。XML 格式提供了一种将不同的后端过程组合在一起的方法,这些过程一直很难改变。XML/XSL 还使得到达不同平台上的不同浏览器成为可能。

    假设开发人员发现 XML 最适于管理进入网站的后端数据,让我们看一下如何提取此 XML 数据以及如何将它转换为 HTML。首先,我将看一下 ASP.NET 解决方案。

    ASP.NET 解决方案

    我的朋友 Dan Wahlin ASP.NET 编写了一个基于 XML 的菜单控件Make Web Browsing Easier,它是我所见到的最佳控件之一。(您需要具备 DevX 成员资格才能访问本文。)在经过 Dan 的允许之后,我已经将此代码进行了一些增强并将它打包在 WebControl 中。

    1. 打包在 WebControl 中的基于 XML 的菜单

    WebControl 随后以可重用的方式嵌入到 ASP.NET 页中:

    XmlMenu:XmlMenuBar id="MyMenus" runat="Server" src="menus.xml">

    /XmlMenu:XmlMenuBar>

    现在,让我们看一下此控件的实现。呈现 HTML 菜单的逻辑首先从重写 WebControl Render() 虚拟方法开始。

        protected override void Render(HtmlTextWriter output) {

            XmlDocument doc = (XmlDocument)Context.Cache["Master"];

            if (doc == null)

            {

              string filepath = Context.Server.MapPath(_src);

              doc = new XmlDocument();

              doc.Load(filepath);

              ResolveSubMenus(doc);

              Context.Cache.Insert("Master", doc,

                  new CacheDependency(filepath));

            }

            RenderMenuBar(doc, output);

        }

    此处的通用算法是:加载 menu.xml 文件,解析此文件中的任何外部引用,然后将主菜单文档缓存到内存中,以便提高随后的请求速度。这种缓存功能是 ASP.NET 的一个重要功能。

    解析外部子菜单是通过以下方法来完成的:使用 XPath 遍历文档,查找 src 属性,加载子菜单文档,然后将它们合并到主文档中:

        void ResolveSubMenus(XmlDocument doc)

        {

            DocumentNavigator nav = new DocumentNavigator(doc);

            nav.Select("//*[@src]"); // XPath for finding all src attributes.

            while (nav.MoveToNextSelected())

            {

                string src = nav.GetAttribute("src");

                XmlDocument submenudoc = new XmlDocument();

                submenudoc.Load(Context.Server.MapPath(src));

                DocumentNavigator nav2 = new DocumentNavigator(submenudoc);

                nav2.MoveToDocumentElement();

                nav.MoveChildren(TreePosition.LastChild, nav2);

            }

        }

    注:预编译 XPath 表达式的功能是 .NET 框架中新 XML 类的一个重要功能,这为我们带来了更好的性能。

    接着,我们采用这个完全解析的主 XmlDocument,并将 HTML 输出结果写入该 ASP.NET 框架原来传递到的HtmlTextWriter 中。顶层菜单栏通过使用预定义的级联式样式表 (CSS) 类以 HTML 表的形式写出,以便菜单的总体外观可通过一个单独的 .css 文件控制。

    这需要两个步骤:写出顶层菜单栏项,然后写出菜单本身。这些菜单是用隐藏的 DIV 元素写出的,一些客户端 ECMAScript 代码会以动态方式弹出这些元素。

        object _linkexpr; // pre-compiled XPath expressions.

        object _nameexpr;

     

        void RenderMenuBar(XmlDocument doc, HtmlTextWriter output)

        {

            output.Write("table width='100%' class=clsMenuBar border='0' ");

            output.Write("cellspacing='0' cellpadding='0'>\ntr>td>\n");

            output.Write("table class=clsMenuBar border=0 cellspacing=0 ");

            output.Write("cellpadding='0'>\n");

            output.Write("tr align='left' valign='middle'>\ntd>  /td>\n");

     

            DocumentNavigator nav = new DocumentNavigator(doc);

            _linkexpr = nav.Compile("link");

            _nameexpr = nav.Compile("name");

     

            nav.MoveToDocumentElement();

            RenderMenuBarItems(nav, output);

            output.Write("/tr>/table>\n/td>/tr>/table>\n");  

            nav.MoveToDocumentElement();

            RenderMenus(nav, output);

        }

    通过遍历根元素的子级,为菜单栏生成表单元格和特殊的可自定义菜单栏空格单元格,来呈现菜单栏项。它还生成要与鼠标进行交互的 DHTML,以便在鼠标移到菜单上时会弹出子菜单。使用各种 XPath 表达式,可以弹出根层菜单项中的每个菜单的名称和链接部分。正如您所看到的一样,此代码相当长。为了将字符串串联减少到最少,输出调用是以一种相当冗长的方式来完成的。这将使性能大约提高 10%

        void RenderMenuBarItems(XmlNavigator nav, HtmlTextWriter output)

        {

            int i = 0;   

            nav.Select("child::*"); // select child elements

            bool first = true;

     

            if (!nav.MoveToFirstChild()) return;

     

            do

            {

                if (nav.Name == "menu")

                {

                    if (! first) {

                        output.WriteLine("td class='clsMenuBarCell' nowrap>");

                        output.Write(_barSpacer);

                        output.Write("/td>");

                    }

                    string name = "menu" + i++;

                    output.Write("td class='clsMenuBarCell' nowrap>");

                    output.Write("a id='start' class='clsMenuBarCell' ");

                    output.Write("style='text-decoration: none; cursor: hand'");

     

                    string target = "";

                    string href = "";

                    if (nav.SelectSingle(_linkexpr)) // is it also a link ?

                    {

                        target = nav.GetAttribute("target");

                        href = nav.InnerText;

                        nav.MoveToParent();

                    }

                    if (HasSubMenu(nav)) // submenu ?

                    {

                        output.Write("onmouseover='startIt(\"");

                        output.Write(name);

                        output.Write("\",this,0)' ");

                    }

                    else if (href != "")

                    {

                        output.Write(" onMouseOver=\"stateChange('',this,'');hideDiv(0);\" ");

                        output.Write("onMouseOut=\"stateChange('',this,'');\" ");

                    }

                    if (href != "")

                    {

                        output.Write("href='");

                        output.Write(href);

                        output.Write("' onClick=\"Select('");

                        output.Write(href);

                        output.Write("','");

                        output.Write(target);

                        output.Write("')\" ");

                    }

                    output.Write(">");

                    nav.SelectSingle(_nameexpr);

                    string text = nav.InnerText;

                    output.Write(text);// grab the menu name

                    nav.MoveToParent();                       

                    first = false;

                }

            }

            while (nav.MoveToNext());

        }

    接着,将菜单呈现在隐藏的 DIV 元素中。赋予每个菜单一个唯一的 ID,客户端 ECMAScript 代码使用这些 ID 将菜单项以及与之相关的子菜单联系在一起:

        void RenderMenus(XmlNavigator nav, HtmlTextWriter output)

        {

            int i = 0;

            if (!nav.MoveToFirstChild()) return;

            do

            {

                if (nav.Name == "menu")

                {

                    string name = "menu" + i++;