代码改变世界

为SharePoint顶部链接开发自定义数据源

2012-07-16 09:20 by Windie Chai, ... 阅读, ... 评论, 收藏, 编辑

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/&quot;">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模块,查看其属性,点击“安全控制项”(又一个烂翻译)旁边的省略号按钮。点击“添加”并依下图配置安全控件:


image

接下来将这个模块添加到合适的Feature里,部署吧。

image

看起来似乎有些诡异,其实这是因为AspMenu控件的默认设置导致的,在本文开头的标记中你可以看到,AspMenu的StaticDisplayLevels属性默认值为2,也就是说它会显示两层静态菜单。通常我们只希望显示一层顶级菜单,那么可以用SharePoint Designer将这个值修改为1。

修改后的效果如下:

image

本文介绍的方法是用来修改SharePoint母版页中默认包含的顶部链接导航栏的数据源的,但如果你有特殊的需求需要单独写一个导航菜单,也可以使用AspMenu,毕竟可以避免许多前端JavaScript和Css方面的工作,而数据源部分,可以用本文提到的方法稍作变通(可以不需要DelegateControl了)。