从.NET Framework2.0开始,VS2005集成开发环境中就为开发人员提供了三个导航控件:树状目录(TreeView)、菜单(Menu)和导航条(SiteMapPath)。这三个控件可通过Items属性添加节点元素,也可以通过SiteMapDataSource控件与站点地图文件进行绑定显示页面导航。但好多开发人员并不喜欢使用它们,原因是--“这三个控件看似使用简单,但不能满足开发中的多变需求”。其实、这三个控件完全可以满足开发中的各种需求,只是我们对他们了解的太少,甚至还不知道它的的优秀功能。如果你想了解更多请往下看。
本篇文章的主要内容:
1.使用SiteMap类访问站点地图数据。
2.自定义导航模板。
3.不同授权用户界面中,导航控件的节点显示与隐藏。
4.为站点地图编写自定义的数据提供程序,让导航控件绑定显示数据库中的内容。
web.SiteMap内容
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
<siteMapNode url="~/Default2.aspx" title="系统首页" description="">
<siteMapNode url="~/Admin/Default.aspx" title="管理员" description="" >
<siteMapNode url="~/Admin/Index.aspx" title="管理员首页"></siteMapNode>
<siteMapNode url="~/Admin/Users.aspx" title="用户管理"></siteMapNode>
<siteMapNode url="~/Admin/Other.aspx" title="其它管理"></siteMapNode>
</siteMapNode>
<siteMapNode url="~/Tester/Default.aspx" title="测试员" description="" >
<siteMapNode url="~/Tester/Log.aspx" title="工作日志"></siteMapNode>
<siteMapNode url="~/Tester/Other.aspx" title="测试报告"></siteMapNode>
</siteMapNode>
<siteMapNode url="~/AuthUsersOnly/Default.aspx" title="个人信息">
<siteMapNode url="~/AuthUsersOnly/ChangePWD.aspx" title="修改密码"></siteMapNode>
<siteMapNode url="~/AuthUsersOnly/Other.aspx" title="基本信息"></siteMapNode>
</siteMapNode>
<siteMapNode url="~/All/" title="友情链接" roles="*">
<siteMapNode url="http://www.xinhuanet.com" title="新华网" roles="*"></siteMapNode>
<siteMapNode url="http://www.china.com" title="中华网" roles="*"></siteMapNode>
</siteMapNode>
</siteMapNode>
</siteMap>
一、使用SiteMap类访问站点地图数据
在System.Web命名空间下有SiteMap类,它能以只读的方式编码访问站点地图。SiteMapPath控件和SiteMapDataSource控件都是使用SiteMap类读取站点地图文件的。
SiteMap类拥有两个重要的属性:RootNode和CurrentNode,这两个属性都是SiteMapNode类型。
SiteMapNode类代表在站点地图中定义的每个节点,它包含Title、Url和Description三个属性,另外还包含四个与其它节点实例相关的属性:
ParentNode:(返回SiteMapNode)当前节点的对象的父节点,如果已经是顶级节点则该属性为Null
ChildNodes:(返回SiteMapNodeCollection)当前节点下的子节点
NextSibling:(返回SiteMapNode)
PreviousSibling:(返回SiteMapNode)
在控制页面导航时,我们可以使用SiteMap类建造自己的导航方式。比如:我们想要在页面底部加入“前一节点”、“后一节点”、“上一级节点”三个导航链接,并且在上面显示对应节点的标题。
《图1》
当点击这三个链接的时候分别转入到对应的页面中去。
我们可以在母版页底部加入三个超连接
如图所示:
做在母版页中就可以省去每个页面都去做重复的工作了。
然后再在母版页的Page_Load中编写代码:
(原创:灰灰虫的家http://hi.baidu.com/grayworm)
二、使用模板自定义导航控件的显示
凡是使用过Repeater、DataList或GridView的朋友都不会对模板陌生,在Menu和SiteMapPath中也可以自定义模板。
在默认情况下,Menu和SiteMapPath会把站点地图中的节点显示为标准的超连接,当用户点击它们的时候,会跳转到目标页面中去。如果想在点击节点后执行一定服务端代码处理的话,就不能再把导航节点显示为超连接了。我们可以使用模板来把节点做成超连接按钮,然后再为该按钮编写绑定与事件。
Menu只提供了一种模板,SiteMapPath提供了四种模板。
三、导航控件与用户授权
应用程序可能需要涉及到授权的问题,允许某些用户访问其中的某些页面,而不是让所有用户都能访问所有的页面。在此之前,我们往往会把程序做成“前台”与“后台”两部分,“前台”部分是供大部份用户浏览的,而“后台”部分是供管理员进行管理使用的,但对于角色较多、功能综合的系统就不能这样简单的授权了。比如:我们做了一个办公系统的软件,市场部的人员、开发部的人员、售后维护人员和财务部的人员所能访问到的页面应当是不一样的,这就不是简单的“前台”和“后台”能够解决的。有的系统可能会涉及到几十种甚至上百种不同功能模块,这些模板又要分发给几十个不同的角色、上千个用户,当用户登录后就不应当把系统所有功能模块都显示出来,应当只显示与该用户相关的功能模块。
下面我们就看一下,如何把导航控件与用户角色相关联起来。
(一)配置程序的Membership 和Roles
要想使导航控件与用户角色相关联,首先应当配置应用程序的Membership与Roles。
1.aspnet_regsql注册数据库
2.修改配置文件
《图7》
3.向数据库中添加两个角色、四个用户
Administrator角色:管理员。
Tester角色:测试人员
Superman用户:拥有Administrator角色和Tester角色
Admin用户:拥有Administrator角色
Mr.Tester用户:拥有Tester角色
AverageUser用户:不属于任何角色
(二)在建立测试项目
1.建立一个登录页面Default.aspx
2.建立一个母版页,在母版页中加入导航控件Menu和SitemapDataSource控件,下面所有的页面都基于此母版页建立
3.建立主页面Default2.aspx
4.建立三个文件夹Admin、Tester和AuthUsersOnly,分别授权给Administrator角色、Tester角色和合法登录用户,并在子文件夹中建立相关页面(参见上面站点地图的定义)
现在运行测试程序,登录后发现所有登录的用户进入主页面后都会显示站点地图中的所有节点,但点击未授权的导航节点时,则跳转到登录页面了。这样用户感觉不好,应当只显示该用户所具有的权限导航节点。请看下面最精彩的地方
(三)配置站点导航与角色进行关联
《图8》
只需要在XmlSiteMapProvider配置元素中加入securityTrimmingEnabled="true"就可以了。
下面再运行测试程序
《图12》
从上面的图中我们看出导航控件显示的内容的确是与用户授权进行了关联,但有的节点并不适合这种关联,例如:
1.如果导航节点中的目标连接到外部资源,不是本站路径。
2.如果节点需要对所有用户都显示,包括未登录的用户。
以上两种情况如果还是启用授权与导航控件的关联,那这类导航节点将永远不会被显示出来。
那如何在保持权授权与导航控件关联的基础上,解决上面这两种情况呢?很简单,只需要你在站点地图文件中找到对应的节点,并在该节点中加入roles="*"属性。
四、为导航控件创建自定义的数据提供程序
导航控件使用的是Provider模型实现对导航数据的显示,它默认提供了对XML格式数据访问的的XmlSiteMapProvider,用户也可以定义自己的Provider向导航控件提供数据。
虽然默认的XmlSiteMapProvider能够把XML文件中的数据显示在页面上,但有的时候我们更需要把数据从数据库中加载到导航控件中显示出来。这其实不难,只要我们编写自己的SqlSiteMapProvider就可以了。
要编写自定义的Provider程序只需要派生StaticSiteMapProvider类,并重写该类中有两个方法
GetRootNodeCall():返回站点地图的根节点
BuildSiteMap():构建站点地图并返回根节点
除了这两个方法外,我们还常用到的就是Initialize()方法。当我们在Web.Config中注册我们自定义的Provider时,当程序运行时就会触发该Provider的Initialize()方法,初始化该Provider,并把Web.Config中配置的names和values等内容传入Initialize(),在Initialize()方法中把传入的内容保存到类的成员变量中去,以便其它方法的使用。
下面我们编写一个SqlTreeProvider,从数据库中读取行政区的数据,向导航控件提供数据。
数据库结构如下:
下面是我们编写的SqlTreeProvider代码
public class SqlTreeProvider : StaticSiteMapProvider
{
//连接字符串,从web.config中取得连接字符串的值,以知道该从哪个数据库中读取数据
protected string _ConnectionString;
//根节点对象
protected SiteMapNode _Root;
public SqlTreeProvider()
{
}
//从web.config中读取内容,初始化成员变量。
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection attributes)
{
base.Initialize(name, attributes);
_ConnectionString = attributes["connectionString"].ToString();
}
//用于递归读取行政区的方法
private void CreateNode(SiteMapNode parentNode)
{
if (parentNode == null)
return;
List<NodeData> list = new NodeDA(_ConnectionString).Select(parentNode.Key);
foreach (NodeData data in list)
{
SiteMapNode node = new SiteMapNode(this, data.Code, "", data.Name);
this.AddNode(node,parentNode);
CreateNode(node);
}
}
//构建站点地图对象
public override SiteMapNode BuildSiteMap()
{
if (_Root == null)
{
_Root = new SiteMapNode(this, "0001", "", "中国");
AddNode(_Root);
CreateNode(_Root);
}
return _Root;
}
//返回根节点对象
protected override SiteMapNode GetRootNodeCore()
{
return _Root;
}
//内部类,实体类
class NodeData
{
public string Code { get; set; }
public string Name { get; set; }
public string ParentCode { get; set; }
}
//内部类,数据访问类
class NodeDA
{
private SqlConnection _Conn;
private SqlCommand _Cmd;
public NodeDA(string connectionString)
{
string cs = System.Configuration.ConfigurationManager.ConnectionStrings[connectionString].ConnectionString;
_Conn = new SqlConnection(cs);
_Cmd = _Conn.CreateCommand();
}
public List<NodeData> Select(string parentCode)
{
List<NodeData> list = new List<NodeData>();
_Cmd.CommandText = "select * from chinastates where parentareacode = @parentareacode";
_Cmd.Parameters.Clear();
_Cmd.Parameters.AddWithValue("@parentareacode",parentCode);
try
{
_Conn.Open();
SqlDataReader dr = _Cmd.ExecuteReader();
while (dr.Read())
{
NodeData data = new NodeData
{
Code = dr["areacode"].ToString(),
Name = dr["areaname"].ToString(),
ParentCode = dr["parentareacode"].ToString()
};
list.Add(data);
}
}
finally
{
_Conn.Close();
}
return list;
}
}
}
上面我们编写完成SqlTreeProvider,如果让系统能够识别它,并利用它加载数的话,还应录在web.config中注册它。
在页面中使用时需要为SiteMapDataSource指定SiteMapProvider="SqlSiteMapProvider"。
运行结果: