SharePoint母版页里自带了一个顶部链接导航栏,我们可以在设置页面配置这个导航栏的内容,但如果你想要从某些配置(比如XML或者SharePoint List)来读取数据并呈现在这个导航栏里的话,就需要一些开发工作,让我们来看看需要做哪些事情。
打开母版页,可以找到如下用来表示顶部链接的标记:
<SharePoint:AspMenu ID="TopNavigationMenuV4" Runat="server" EnableViewState="false" DataSourceID="topSiteMap" AccessKey="<%$Resources:wss,navigation_accesskey%>" UseSimpleRendering="true" UseSeparateCss="false" Orientation="Horizontal" StaticDisplayLevels="2" MaximumDynamicDisplayLevels="5" SkipLinkText="" CssClass="s4-tn"/> <SharePoint:DelegateControl runat="server" ControlId="TopNavigationDataSource" Id="topNavigationDelegate"> <Template_Controls> <asp:SiteMapDataSource ShowStartingNode="False" SiteMapProvider="SPNavigationProvider" id="topSiteMap" runat="server" StartingNodeUrl="sid:1002"/> </Template_Controls> </SharePoint:DelegateControl>
可以看出顶部链接是一个AspMenu控件,它的DataSourceID是topSiteMap,是一个SiteMapDataSource控件,好在这个控件被包含到一个DelegateControl里,所以我们只需要开发一个ID同样为topNavigationDelegate的DelegateControl,就可以达到替换数据源的效果。
AspMenu需要层级结构的数据源,所以我们先来写一个表示菜单项分层集合类:
public class MenuHierarchicalEnumerable : ArrayList, IHierarchicalEnumerable { public MenuHierarchicalEnumerable() : base() { } public IHierarchyData GetHierarchyData(object enumeratedItem) { return enumeratedItem as IHierarchyData; } }
然后实现表示菜单项的实体类,这个类需要实现IHierarchyData和INavigateUIData两个接口:
public class Menu : IHierarchyData, INavigateUIData { public string Name { get; set; } public string NavigateUrl { get; set; } public string Description { get { return Name; } } public string Value { get { return NavigateUrl; } } public object Item { get { return this; } } public string Path { get { return NavigateUrl; } } public string Type { get { return typeof(Menu).FullName; } } public Menu[] SubMenus { get; set; } public bool HasChildren { get { return SubMenus != null && SubMenus.Length > 0; } } public IHierarchicalEnumerable GetChildren() { var children = new MenuHierarchicalEnumerable(); if (this.HasChildren) { children.AddRange(this.SubMenus); } return children; } public IHierarchyData GetParent() { return null; } }
实现INavigateUIData接口的目的是让AspMenu将数据正确识别为链接,在实现这个接口的时候,要注意它的所有属性都必须不能为空,否则会抛出异常。
接着是一个分层数据源的视图类,我们需要重写它的Select方法,并返回分层菜单项集合。这里我丑陋的硬编码了菜单项集合,在实际工作中,可以从XML或者SharePoint List等位置读取数据:
public class MenuDataSourceView : HierarchicalDataSourceView { public MenuDataSourceView(string viewPath) { } public override IHierarchicalEnumerable Select() { var rootMenu = Enumerable.Range(1, 10).Select(i => new Menu() { Name = "Menu 1." + i.ToString(), NavigateUrl = "#" }).ToArray(); foreach (var menu in rootMenu) { menu.SubMenus = Enumerable.Range(1, 5).Select(i => new Menu() { Name = "Menu 2." + i.ToString(), NavigateUrl = "#" }).ToArray(); foreach (var subMenu in menu.SubMenus) { subMenu.SubMenus = Enumerable.Range(1, 3).Select(i => new Menu() { Name = "Menu 3." + i.ToString(), NavigateUrl = "#" }).ToArray(); } } var result = new MenuHierarchicalEnumerable(); result.AddRange(rootMenu); return result; } }
我硬编码了一组菜单项,顶级菜单10个,每个下面有5个二级菜单,而每个二级菜单下面又有3个三级菜单。
最后是真正的数据源控件:
public class MenuDataSource : HierarchicalDataSourceControl { public MenuDataSource() : base() { } protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath) { return new MenuDataSourceView(viewPath); } }
到此,代码开发完毕,下面是部署这些代码所需要的工作。
首先添加一个名为“MenuDataSourceDelegateControl”的自定义模块,删掉自动生成的Sample.txt,将Elements.xml修改如下的内容:
<?xml version="1.0" encoding="utf-8"?> <Elements xmlns="<a href="http://schemas.microsoft.com/sharepoint/"">http://schemas.microsoft.com/sharepoint/"</a>> <Control Id="TopNavigationDataSource" ControlAssembly="VisualWebPartDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=496e7a1c31c1f5a3" ControlClass="WindStyle.Demo.MenuDataSource"> <Property Name="ID">topSiteMap</Property> </Control> </Elements>
这段XML表示,在Feature激活之后,会用指定的程序集和类来创建一个MenuDataSource控件,并将它的ID设置为topSiteMap(既AspMenu的DataSourceID),然后用这个控件来替代母版页中名为TopNavigationDataSource的DelegateControl,那么AspMenu的数据源也就相当于被替换了。
到这一步还没有完全结束,因为MenuDataSource是一个控件,我们需要将它标识为安全控件才能够在SharePoint中使用。
右键单击刚才创建的MenuDataSourceDelegateControl模块,查看其属性,点击“安全控制项”(又一个烂翻译)旁边的省略号按钮。点击“添加”并依下图配置安全控件:
接下来将这个模块添加到合适的Feature里,部署吧。
看起来似乎有些诡异,其实这是因为AspMenu控件的默认设置导致的,在本文开头的标记中你可以看到,AspMenu的StaticDisplayLevels属性默认值为2,也就是说它会显示两层静态菜单。通常我们只希望显示一层顶级菜单,那么可以用SharePoint Designer将这个值修改为1。
修改后的效果如下:
本文介绍的方法是用来修改SharePoint母版页中默认包含的顶部链接导航栏的数据源的,但如果你有特殊的需求需要单独写一个导航菜单,也可以使用AspMenu,毕竟可以避免许多前端JavaScript和Css方面的工作,而数据源部分,可以用本文提到的方法稍作变通(可以不需要DelegateControl了)。