16.5 处理站点地图文件

前文阐述了站点地图文件的一些简单使用方法,使你无须编写任何后台代码就可以在导航控件里面显示站点地图文件的内容。其实,除了这些手动设置方式之外,还可以通过编程的方式来使用ASP.NET站点导航。下面就来讨论如何以编程的方式来处理站点地图文件,以及如何自定义站点地图信息。

16.5.1 编程枚举站点地图节点

当Web应用程序运行时,ASP.NET会创建一个反映站点地图结构的SiteMap对象。其中,SiteMap类是站点的导航结构在内存中的表示形式,导航结构由一个或多个站点地图提供程序提供。常用属性如表16-8所示。

figure_0557_0442

创建好SiteMap对象之后,SiteMap对象则依次公开包含站点地图中每个节点的属性的SiteMapNode对象的集合((SteMapNode类表示分层的站点地图结构中的一个节点,常用属性如表16-9所示)。这时候,导航控件(如SiteMapPath控件)使用SiteMap和SiteMapNode对象来自动呈现适当的链接。因此,可以在自己的代码中使用SiteMap和SiteMapNode对象来创建自定义导航。

figure_0557_0443

为了能够在这里更好地演示SiteMap和SiteMapNode的用法,下面仍然以前面的示例为基础。在内容页Default.aspx里面添加两个Label控件。其中,txt_currentNode控件用于显示当前节点的名称(即地图文件中的title属性)与异常信息,而txt_childNode控件用于显示子节点的名称。见代码清单16-8所示。

代码清单16-8 Default.aspx


<%@Page Title=""Language="C#"MasterPageFile="~/Home.Master"

AutoEventWireup="true"CodeBehind="Default.aspx.cs"

Inherits="_16_3.Default"%>

<asp:Content ID="Content1"

ContentPlaceHolderID="ContentPlaceHolder1"runat="server">

<i>当前节点:</i>

<br/>

<asp:Label ID="txt_currentNode"runat="Server"></asp:Label>

<br/>

<i>子节点:</i>

<br/>

<asp:Label ID="txt_childNode"runat="Server"></asp:Label>

</asp:Content>


Default. aspx页面设计好之后,就需要在它的Page_Load事件里添加相关处理代码了。如果当前页在站点地图文件中列出,则显示相关节点名称信息。如果当前页没有在站点地图文件中列出,则使用SiteMap对象的第一行代码将引发NullReferenceException异常。如下面的代码所示:


namespace_16_3

{

public partial class Default:System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

try

{

string node="";

//显示当前节点的Title

txt_currentNode.Text=SiteMap.CurrentNode.Title;

//如果确定当前节点有子节点

if(SiteMap.CurrentNode.HasChildNodes)

{

foreach(SiteMapNode childNodesEnumerator in

SiteMap.CurrentNode.ChildNodes)

{

//显示每个子节点的Title

node+=childNodesEnumerator.Title+"<br/>";

}

}

txt_childNode.Text=node;

}

catch(System.NullReferenceException ex)

{

txt_currentNode.Text=ex.ToString();

}

catch(Exception ex)

{

txt_currentNode.Text=ex.ToString();

}

}

}

}


运行代码清单16-8,结果如图16-22所示。

figure_0559_0444

图 16-22 Default.aspx显示结果

16.5.2 编程修改内存中的站点地图节点

在日常开发中,网站使用最多的是动态URL,其中包含作为查询字符串附加的信息。例如,新闻组或论坛的站点可能包含指向论坛或小组的静态URL以及每篇文章的动态URL。一篇文章的URL可能为下面的格式:


http://www.comesns.com/news/ShowArticle.aspx?ArticleID=100


通过更新站点地图来列出每篇文章的URL并不是一种很有效的办法,原因是不能以编程方式将节点添加到站点地图中。但是,当用户查看文章时,可以使用SiteMapPath控件显示上溯至根节点的导航路径,并在该路径的各个链接中动态附加查询字符串,以标识文章、论坛或组等。例如,到上述文章的导航路径可能如下所示:


主页>论坛列表>文章列表


因此,站点地图“文章”节点的静态URL属性可能设置为http://www.comesns.com/news/ShowArticle.aspx,但是在内存中,可以修改SiteMapPath控件中的URL,以标识论坛和特定的文章。

为了更好地演示这种功能,首先为SiteMapPath控件添加一个RenderCurrentNodeAsLink属性。这样,当运行一个示例时,将光标置于SiteMapPath控件中链接的上方,可查看URL的更改。如下面的代码所示:


<asp:SiteMapPath ID="SiteMapPath1"runat="server"

RenderCurrentNodeAsLink="true">

</asp:SiteMapPath>


在窗体设计好SiteMapPath控件后,就可以直接使用SiteMapResolve事件在内存中更改站点地图节点了。如下面的代码所示:


public partial class Default:System.Web.UI.Page

{

protected void Page_Load(object sender, EventArgs e)

{

SiteMap.SiteMapResolve+=

new SiteMapResolveEventHandler(this.ExpandForumPaths);

}

private SiteMapNode ExpandForumPaths(Object sender,

SiteMapResolveEventArgs e)

{

SiteMapNode currentNode=SiteMap.CurrentNode.Clone(true);

SiteMapNode tempNode=currentNode;

int articleID=GetArticleID();

if(0!=articleID)

{

tempNode.Url=tempNode.Url+"?ArticleID="

+articleID.ToString();

}

return currentNode;

}

private int GetArticleID()

{

return 128;

}

}


最后需要说明的是,此示例只是修改内存中的站点地图节点,并不在Web.sitemap文件中添加SiteMapNode项目,并且Web.sitemap文件只能手动编辑。

16.5.3 自定义站点地图信息

从上面的地图文件中可以看出,所有站点地图的节点所提供的信息都只有标题((ttle)、描述((dscription)和URL。这些都是你所希望提供的信息中的最关键部分,也是必须提供的基础信息。如下面的代码所示:


<siteMapNode title="处理器"description="处理器信息"

url="~/Cpu.aspx"/>


然而,有时可能会因为多种原因希望插入额外的节点数据。这些额外信息可以是要显示的描述性信息或者描述链接如何工作的上下文信息等。例如,可以添加指定目标框架的特性或指定链接需要在弹出窗口中打开。

相对于这些自定义信息,XML站点地图的架构提供了完全开放的支持。也就是说,可以自由地在siteMapNode里插入自定义的节点数据。下面的代码显示了一个使用Target特性指定链接要打开的框架的站点地图。在这个示例里,一个链接的目标被设置为_blank,因此它将在一个新(弹出)的浏览器窗口中打开。


<siteMapNode title="处理器"description="处理器信息"

url="~/Cpu.aspx"target="_blank"/>


添加好自定义节点之后,有两种方法可以来处理它:

1)在导航控件里使用模板,直接绑定到添加的新特性。

2)在后台用程序进行处理,如下面的示例代码所示:


Protected void TreeView1_TreeNodeDataBound(

object sender, TreeNodeEventArgs e)

{

e.Node.Target=(((SteMapNode)e.Node.DataItem)["target"];

}


注意,在这里不能通过强类型属性获取自定义特性,相反,必须按名称通过SiteMapNode索引器获取。