目前你看到的所有的站点地图中为节点所提供的信息只有标题、描述、URL。然而,XML 的结构是开放的,也就是说,你可以自由的插入含有你自己数据的自定义特性。例如,你可以添加指定目标框架的特性或指定链接需要在弹出窗口中打开。现在,唯一的问题是你必须自行处理这些信息。换句话说,你必须配置用户界面来让它使用这些额外信息。

下面的代码显示了一个使用 Target 特性指定链接要打开的框架的站点地图:

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
  <siteMapNode title="Home" description="Root" url="~/Default.aspx">
    <siteMapNode title="Products" description="Our products" url="~/Products.aspx" target="_blank" >
    ...

现在你的代码中有几个选择。如果导航控件里使用模板,可以直接绑定到已添加的新特性。如果导航控件不支持模板(或者你不愿意创建模板),就要采用另一个方法。TreeView 和 Menu 都在每个绑定项绑定(DataBound 事件)时引发一个事件:

protected void treeNav_TreeNodeDataBound(object sender, TreeNodeEventArgs e)
{
    // 这里不能通过强类型的属性获得 target 特性值,SiteMapNode 类没有这个属性
    // 必须按名称通过索引器获取
    e.Node.Target = ((SiteMapNode)e.Node.DataItem)["target"];
}

创建自定义的 SiteMapProvider

要真正改变 ASP.NET 导航模型的工作方式,你必须创建自己的站点地图提供程序。你可能会因为以下几个原因选择自定义的站点地图提供程序:

  • 需要把站点地图保存在不同的数据源(如关系型数据库)中。
  • 要按不同的架构保存站点地图信息,它和 ASP.NET 预期的 XML 格式不同。
  • 需要一个高度动态的站点地图,随时可进行创建。例如,你可能希望按当前用户、查询字符串参数等产生不同的站点地图。
  • 需要改变 XmlSiteMapProvider 实现中的某一个限制。例如,你可能希望具有带重复 URL 的节点。

实现自定义站点地图提供程序时,你有两个选择。

  • 继承 System.Web 命名空间的抽象基类 SiteMapProvider (从头实现一个新的提供程序)。
  • 继承 StaticsSiteMapProvider 类(它提供了很多方法的基本实现,包括保存和搜索节点的逻辑)。

随后,在这里我将介绍一个允许在数据库中保存站点地图信息的自定义提供程序。

1. 在数据库中保存站点地图信息

下图只是重复了前一章所看到的站点地图数据。

对于这个解决方案,站点地图程序不应直接访问表。应使用存储过程。这增加了灵活性,而且你以后可以用不同的架构保存导航信息,只要存储过程返回的表包含预期的列即可。下面是本例中使用的存储过程:

create proc GetSiteMap as
select * from SiteMap order by ParentID,Title

2. 创建站点地图提供程序

站点地图没有改变站点地图导航的底层逻辑,所以可以不必由 SiteMapProvider 继承并重新实现所有的追踪和导航行为(这个工作更乏味),而直接从 StaticsSiteMapProvider 继承。

首先,修改配置文件 web.config 文件,一会提供程序需要读取相关的配置:

<system.web>
  <siteMap defaultProvider="SqlSiteMapProvider">
    <providers>
      <add name="SqlSiteMapProvider" type="SqlSiteMapProvider" providerName="System.Data.SqlClient"
         connectionString="Data Source=localhost;Initial Catalog=Northwind;Integrated Security=SSPI"
         storedProcedure="GetSiteMap"/>
      </providers>
  </siteMap>
</system.web>

这里附上自定义站点地图提供程序的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Common;
using System.Data;
 
public class SqlSiteMapProvider : StaticSiteMapProvider
{
    // 首先,需要改写 Initialize() 方法,读取配置文件与站点地图相关的信息
    // 提供程序需要读取下面 3 个信息并保存它们留待稍后使用
    private string connectionString;
    private string providerName;
    private string storedProcedure;
 
    private bool initialized = false;
    public virtual bool IsInitialized
    {
        get { return initialized; }
    }
 
    public override void Initialize(string name,
        System.Collections.Specialized.NameValueCollection attributes)
    {
        if (!IsInitialized)
        {
            base.Initialize(name, attributes);
 
            providerName = attributes["providerName"];
            connectionString = attributes["connectionString"];
            storedProcedure = attributes["storedProcedure"];
 
            if (string.IsNullOrEmpty(providerName))
            {
                throw new ArgumentException("The provider name is not found.");
            }
            if (string.IsNullOrEmpty(connectionString))
            {
                throw new ArgumentException("The connection string is not found.");
            }
            if (string.IsNullOrEmpty(storedProcedure))
            {
                throw new ArgumentException("The stored procedure is not found.");
            }
            initialized = true;
        }
    }
 
 
    // 真正的工作在 BuildSiteMap() 方法中,它创建构造导航树的 SiateMapNode 对象
    // 在应用程序的生命周期里通常只创建一次并多次重用它
    // 因此,提供程序必须在内存里保存它
    private SiteMapNode rootNode;
 
    public override SiteMapNode BuildSiteMap()
    {
        // 因为多个页面会共享站点地图提供程序的同一个实例
        // 因此,在更新任何共享信息(如内存中的导航树)前最好锁定它
        lock (this)
        {
            // Don't rebuild the map unless using caching.
            // If your site map changes often, consider using caching
            if (rootNode == null)
            {
                // Start with a clean slate
                Clear();
 
                // 获取数据库信息保存在 DataSet 中(因为需要往返导航遍历,不可用 DataReader)
                DbProviderFactory provider = DbProviderFactories.GetFactory(providerName);
 
                DbConnection con = provider.CreateConnection();
                con.ConnectionString = connectionString;
 
                DbCommand cmd = provider.CreateCommand();
                cmd.Connection = con;
                cmd.CommandText = storedProcedure;
                cmd.CommandType = CommandType.StoredProcedure;
 
                DbDataAdapter adapter = provider.CreateDataAdapter();
                adapter.SelectCommand = cmd;
 
                DataSet ds = new DataSet();
                adapter.Fill(ds, "SiteMap");
                DataTable dtSiteMap = ds.Tables["SiteMap"];
 
                // 导航 DataTable 创建 SiteMapNode 对象,可以搜索没有父节点的节点(找到根节点)
                DataRow rowRoot = dtSiteMap.Select("ParentID is null")[0];
 
                // 这里也借用了默认模式中 Key 和 Url 值一样的模式(注意: 它们可以不一样)
                rootNode = new SiteMapNode(this, rowRoot["Url"].ToString(), rowRoot["Url"].ToString(),
                    rowRoot["Title"].ToString(), rowRoot["Description"].ToString());
 
                // 填充层次的剩余部分,这里需要递归.
                // 我们使用了一个私有方法 AddChildren(),它每次填充一层.
                string rootID = rowRoot["ID"].ToString();
                AddNode(rootNode);
 
                // fill down the hiearachy(层次,分层)
                AddChildren(rootNode, rootID, dtSiteMap);
            }
        }
        return rootNode;
    }
 
    private void AddChildren(SiteMapNode rootNode, string rootID, DataTable dtSiteMap)
    {
        DataRow[] childRows = dtSiteMap.Select("ParentID = " + rootID);
        foreach (DataRow row in childRows)
        {
            SiteMapNode childNode = new SiteMapNode(this,
                row["Url"].ToString(),
                row["Url"].ToString(),
                row["Title"].ToString(),
                row["Description"].ToString());
            string rowID = row["ID"].ToString();
            AddNode(childNode, rootNode);
 
            AddChildren(childNode, rowID, dtSiteMap);
        }
    }
 
    // 最后填写其他几个获取站点地图信息必需的重载方法
    protected override SiteMapNode GetRootNodeCore()
    {
        return BuildSiteMap();
    }
 
    public override SiteMapNode RootNode
    {
        get
        {
            return BuildSiteMap();
        }
    }
 
    protected override void Clear()
    {
        lock (this)
        {
            rootNode = null;
            base.Clear();
        }
    }
}

现在你的网站不需要建立 Web.Sitemap 文件也可以使用站点地图了。自定义提供程序方便而整洁的插入,新的信息由自定义提供程序提供并到达页面,丝毫看不出底层信息通道已完全改变了。

3. 添加排序

目前,返回按标题字母排序的结果。对于一个实际的站点,你可能需要控制页面出现的顺序。幸好有最简单的解决方案。你不需要动 SiteMapProvider 的代码,只需要在表中增加一个排序的字段,如 OrdinalPosition 并修改存储过程 GetSiteMap 即可。

alter proc GetSiteMap as
select * from SiteMap order by ParentID,OrdinalPosition,Title

这样,首先按父节点进行了分组,然后按照你的控制列进行了排序,当然,这个排序只对同一层的页面有效。

4. 添加缓存

你可能注意到了有一个问题,SqlSiteMapProvider 的一个问题是它永远在内存中保存当前站点地图的根节点。除非应用程序重启(重新编译网站或修改了它的配置),否则就使用同一个站点地图。

如果打算经常修改站点地图,有几个办法可以确保应用程序被通知到变化并刷新站点地图。最好的办法是使用数据缓存把根节点保存一段有限的时间。

<add name="SqlSiteMapProvider" ... cacheTime="600"/>

在提供程序中获取这个时间:

cacheTime = Int32.Parse(attributes["cacheTime"]);

修改后的 BuildSiteMap()在所需的时间内将站点地图信息保存在缓存中:

public override SiteMapNode BuildSiteMap()
{
    SiteMapNode rootNode;
    lock (this)
    {
        rootNode = HttpContext.Current.Cache["rootNode"] as SiteMapNode;
 
        if (rootNode == null)
        {
            ...
 
            HttpContext.Current.Cache.Insert("rootNode", rootNode, null,
                DateTime.Now.AddSeconds(cacheTime), TimeSpan.Zero);
        }
    }
    return rootNode;
}

最后,SqlSiteMapProvider.Clear()需要做少量改动以便从缓存中删除站点地图信息:

protected override void Clear()
{
    lock (this)
    {
        HttpContext.Current.Cache.Remove("rootNode");
        base.Clear();
    }
}

网站导航(自定义站点地图)相关推荐

  1. 网站导航(站点地图)

    一个网站,需要借助某个导航系统来帮助用户从一个页面跳转到另一个页面.我们知道,可以利用母版页帮助网站定义一个包含导航栏的模板,不过,仍然要由你来为导航栏填入内容. 当然可以利用 ASP.NET 的控件 ...

  2. 网站嵌入自定义 google 地图

    Q:想在页面上嵌入一个谷歌地图框架. 而且可以在同一框架中自定义多个标记.如下: A: 这里有一个示例,说明如何向地图添加三个标记: <!DOCTYPE html> <html> ...

  3. 自学Web开发第十二天-基于VB和ASP.NET;页面嵌套、母版、网站导航

    自学Web开发第十二天-基于VB和ASP.NET:页面嵌套.母版.网站导航 页面嵌套 母版页 母版页的建立和使用 访问母版页上的元素 网站导航 站点地图 SiteMapDataSource控件 地图信 ...

  4. 温故知新ASP.NET 2.0(C#)(3) - SiteMap(站点地图)

    [索引页] [源码下载] 温故知新ASP.NET 2.0(C#)(3) - SiteMap(站点地图) 作者:webabcd 介绍 ASP.NET 2.0 中的站点导航提供程序向应用程序中的页公开导航 ...

  5. 【中文】Joomla1.7扩展介绍之Xmap(站点地图生成)

    Xmap  插件分类: Structure & Navigation => Site Map 支持版本:1.5 /1.6 /1.7 关注程度:[最流行的] 所属类型:组件.插件 ★ 本扩 ...

  6. SiteMap(站点地图)

    介绍 ASP.NET 2.0 中的站点导航提供程序向应用程序中的页公开导航信息,使您可以独立于页的实际物理布局定义站点的结构.默认站点导航提供程序基于XML,但通过为站点地图编写自定义提供程序,也可以 ...

  7. 动态站点地图提交百度收录

    站点地图(sitemap)是一个网站的结构化数据,搜索引擎可以通过站点地图迅速了解一个网站的内容,加快搜索引擎收录. 一般来说,站点地图是以.xml结尾的静态化文件,例如个人博客和生化环材网的站点地图 ...

  8. nuxt 如何生成sitemap.xml 动静态站点地图

    前言 sitemap.xml的作用是将我们网站的所有页面都被SEO(浏览器搜索引擎)收录,我们网站的内容更容易被用户搜到,同时增加我们的网站的知名度,排名更靠前.简言之就是用技术做网站推广,所以对于网 ...

  9. 网站导航:如何在线生成自定义的二维码?(要美观不要俗气)

    进入我的主页,查看更多.精选的网站导航! 二维码可直接扫描,内容全是playfulhd.com(文本) 一.草料二维码 点我直达(https://cli.im) 特点: 支持文本.网址.文件.图片.音 ...

最新文章

  1. plot画图等高线contour 与 contourf 及API
  2. Xshell远程登录Ubuntu
  3. 每日一皮:客户被绑,蒙眼,惊问 “想干什么?”
  4. python sqlserver api连接池_非常老的话题 SQLSERVER连接池
  5. spi四种工作模式时序图_SPI总线协议及SPI时序图详解
  6. jQuery数据表和Java集成
  7. drools 7.x 决策表使用
  8. 整合Servlet到Spring容器
  9. Opencv之生成棋盘标定板
  10. java renderer_Java TableCellRenderer显示奇怪的行为
  11. 20050909:女乘客钓男司机?
  12. 美国慈善机构Kars4Kids意外泄露了上万名捐赠者的个人信息
  13. access ea 可以联网吗_如何看待EA在STEAM上推出EA Play(原EA Access会员)?
  14. 2022年湖北师范大学招生简章--成人高等教育高起专、专升本学历提升
  15. 毫米波雷达及其应用精炼介绍
  16. 内存超频时序怎么调_内存时序怎么调
  17. OPC UA 的本质
  18. 楼宇报警器 java程序_智能楼宇防盗报警系统
  19. 爱因斯坦广义相对论:引力是时空的曲率
  20. 在GT4 Client端EndpointReferenceType的标准序列化方法

热门文章

  1. Linux监控工具Spotlight on Unix
  2. for-in和for-of,forEach和Map
  3. 还在痴迷于大数据?未来 “小数据” 会让你大开眼界
  4. bootstrap dialog
  5. HttpServletResponse中sendError与setStatus的区别
  6. Struts2——(3)ValueStack(值栈)
  7. error: Program received signal SIGSEGV, Segmentation fault. (Codeblocks, C++)(2)
  8. java 获取bean 属性_获取javaBean的属性名属性值属性类型
  9. 数据可视化如何做会更好
  10. 大数据可视化的重要性体现在哪里