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

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了)。

One Comment

  1. leo

    看着是很好,怎么把这个放到合适的feature里面去呢?
    新建的工程,直接部署时有一个feature的,这样不行的啊,打不开页面了,直接报错了!

发表评论

电子邮件地址不会被公开。 必填项已用*标注