SharePoint 2010 站点操作菜单
为 SharePoint 2010 站点操作菜单创建包含子菜单的菜单项

引言
SharePoint 已经存在很多年并且越来越受欢迎,尤其是 SharePoint 2010 发布之后。自定义 SharePoint 应用程序时,一项常见的任务是向“站点操作”菜单添加菜单项,以便授权用户可以轻松访问功能。在本文中,我将介绍三种方法,可以将您自己的菜单项、子菜单或弹出菜单添加到“站点操作”菜单。
站点操作菜单
几乎所有处理过 SharePoint 的人都熟悉“站点操作”菜单。它提供了对诸如启用页面编辑之类的功能的访问,或者在 SharePoint 2010 中,可以显示新的 RibbonBar
。SharePoint 2010 还让开发人员对该菜单及其项目有了更多的控制权。default.master
和 v4.master
母版页包含以下标记,用于在适当的每个页面上渲染基本的“站点操作”菜单。
<sharepoint:siteactions id="SiteActions1" runat="server"
accesskey="<%$Resources:wss,tb_SiteActions_AK%>"
menunotvisiblehtml=" " prefixhtml="" suffixhtml="">
<customtemplate>
<SharePoint:FeatureMenuTemplate runat="server" featurescope="Site"
groupid="SiteActions" location="Microsoft.SharePoint.StandardMenu"
useshortid="true">
<SharePoint:MenuItemTemplate id="MenuItem_EditPage" runat="server"
clientonclicknavigateurl="javascript:ChangeLayoutMode(false);"
description="<%$Resources:wss,siteactions_editpagedescriptionv4%>"
imageurl="/_layouts/images/ActionsEditPage.png" menugroupid="100"
sequence="110" text="<%$Resources:wss,siteactions_editpage%>" />
... more items ...
</SharePoint:FeatureMenuTemplate>
</CustomTemplate>
</Sharepoint:iteactions>
向“站点操作”菜单添加附加项的一种方法是,当然,修改母版页并包含另一个 MenutItemTemplate
元素。
MenuItemTemplate
的所有属性都可以在 http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.webcontrols.menuitemtemplate_properties.aspx [^] 中找到,大多数属性都是不言自明的,并且对于任何 WebControl
都是通用的。但是,有几个属性需要解释。
PermissionString
:此属性提供了一个逗号分隔的列表,其中包含来自 SPBasePermission
枚举的项目,用于指定访问菜单项所需的用户权限级别。
PermissionMode
:此属性决定如何应用 PermissionString
中指定的权限,可以是“Any”(任意)或“All”(全部)。
ClientOnClickScript
:此属性控制用户单击菜单项时执行的客户端操作。如代码设置所示,此属性将删除 ClientOnClickNavigateUrl
和 ClientOnClickScriptContainingPrefixedUrl
属性的任何设置。
[DefaultValue(""), Category("Behavior")]
public string ClientOnClickScript
{
get
{
return this.m_clientOnClickScript;
}
set
{
if (value == null)
{
value = string.Empty;
}
this.m_clientOnClickScript = value;
this.ViewState["ClientOnClickScript"] = value;
this.m_clientOnClickNavigateUrl = null;
this.ViewState["ClientOnClickNavigateUrl"] = null;
this.m_clientOnClickScriptContainingPrefixedUrl = null;
this.ViewState["ClientOnClickScriptContainingPrefixedUrl"] = null;
this.clientOnClickUsingPostBackEvent = null;
}
}
ClientOnClickNavigateUrl
:使用此属性,您可以设置一个 URL 来导航,或者一个 JavaScript 函数来执行,当单击菜单项时。如您所见,它设置了 ClientOnClickScript
属性,因此它将覆盖该属性中的任何设置,所以属性应用于元素的顺序将影响用户单击菜单项时发生的情况。
[Category("Behavior"), DefaultValue("")]
public string ClientOnClickNavigateUrl
{
get
{
return this.m_clientOnClickNavigateUrl;
}
set
{
if (value == null)
{
value = string.Empty;
}
value = SPUtility.GetServerRelativeUrlFromPrefixedUrl(value);
value = value.Replace("'", @"\'");
if ((value.StartsWith("javascript:") || value.StartsWith("%"))
|| value.StartsWith("?"))
{
this.ClientOnClickScript = "window.location = '" + value + "';";
}
else
{
this.ClientOnClickScript = "STSNavigate2(event,'" + value + "');";
}
this.m_clientOnClickNavigateUrl = value;
this.ViewState["ClientOnClickNavigateUrl"] = value;
}
}
ClientOnClickScriptContainingPrefixedUrl
:与 ClientOnClickNavigateUrl
类似,此属性允许您设置一个 URL 或一个 JavaScript 函数来执行。属性设置器调用 SPUtility.ReplaceEmbeddedPrefixedUrlsWithServerRelativeUrls
来替换 string
中找到的任何内置前缀,例如 ~site
或 ~sitecollection
,以及服务器相对 URL。再次,设置了 ClientOnClickScript
,并且属性在元素中应用的顺序很重要。
[DefaultValue(""), Category("Behavior")]
public string ClientOnClickScriptContainingPrefixedUrl
{
get
{
return this.m_clientOnClickScriptContainingPrefixedUrl;
}
set
{
if (value == null)
{
value = string.Empty;
}
value = SPUtility.ReplaceEmbeddedPrefixedUrlsWithServerRelativeUrls(value, true);
this.ClientOnClickScript = value;
this.m_clientOnClickScriptContainingPrefixedUrl = value;
this.ViewState["ClientOnClickScriptContainingPrefixedUrl"] = value;
}
}
VisibilityFeatureId
:使用此属性指定菜单项所依赖的功能 ID(guid
)。如果该功能尚未为该网站激活,则菜单项将不会被渲染。
添加子菜单
要添加子菜单,您必须使用 SubMenuTemplate
控件而不是 MenuItemTemplate
。这里应该注意的是,SubMenuTemplate
不能用于提供任何操作,MenuItemTemplate
的 ClientOnClickXXX
属性不可用,它仅仅是其他菜单项的容器。
<submenutemplate
id="MenuItem_Custom"
description="Custom menu with submenu"
runat="server"
text="Custom Menu"
sequence="100"
menugroupid="90"
imageurl="/_layouts/images/settingsIcon.png" />
<menuitemtemplate
id="MenuItem_CustomSub"
description="A submenu item"
runat="server"
text="SubMenu"
sequence="110"
menugroupid="100"
imageurl="/_layouts/images/settingsIcon.png"
clientonclickscript="alert('Submenu item clicked')" />
</submenutemplate />
SubMenuTemplate
当然可以包含其他 SubMenuTemplate
s 来生成多级菜单结构。

虽然此方法可能适用于一次性情况,但它不是可重现的或推荐的做法。对于可重用解决方案,您应该创建并部署 SharePoint 功能。
通过代码创建菜单
通过 Visual Studio 2010 和 SharePoint 2010 的集成,创建功能现在比以前的版本更容易。要开始创建修改“站点操作”菜单的功能,请在 Visual Studio 2010 中创建一个 SharePoint 2010 模块项目,确保选择的是场解决方案而不是沙盒解决方案。项目创建后,您将看到以下结构。可以删除 Sample.txt 文件,因为它未使用。

Elements.xml 文件现在应该如下所示:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Module1">
</Module>
</Elements>
删除 Module
元素,并根据下图替换为 CustomAction
元素:
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
<customaction
id="MANSoftDev_SiteAction"
sequence="1"
controlclass="MANSoftDev.SiteAction.SiteActionCode"
controlassembly="MANSoftDev.SiteAction, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=2e15c7b1150d656d"
rights="ManageWeb"
location="Microsoft.SharePoint.StandardMenu"
groupid="SiteActions">
</customaction>
</elements>
GroupId
和 Location
属性告诉 SharePoint CustomAction
将放置在哪里。在这种情况下,位置是 StandardMenu
,组是 SiteActions
,将其放置在“站点操作”菜单中。Locations
和 Groups
的完整列表可以在 http://msdn.microsoft.com/en-us/library/bb802730.aspx[^] 中找到。Rights 属性是 SPBasePermissions
枚举值的逗号分隔列表。但是,与前面的示例不同,用户必须拥有所有指定的权限,而不是任意权限。ControlAssembly
和 ControlClass
属性是 SharePoint 2010 新增的,允许您指定将控制此 CustomAction
的类。这对于实现至关重要。ControlAssembly
是实现该类的程序集的四部分名称。ControlClass
是类的完全限定名。请注意,它必须包含完整的命名空间,仅仅提供类名是不够的。要生成此类,请向解决方案添加类库项目,并对程序集进行签名,因为它将部署到 GAC。创建项目后,添加对 System.Web
和 Microsoft.SharePoint
的引用,然后,重命名现有的 Class1
文件,或删除它并添加另一个派生自 WebControl
的类,最终得到这个。
namespace MANSoftDev.SiteAction
{
public class SiteActionCode : WebControl
{
}
}
在继续之前,最好将类库程序集添加到部署包中,并将其部署到 GAC,并在 web.config 文件中的 SafeControls
部分添加适当的设置。为此,您需要修改 Visual Studio 2010 项目中的包。打开包设计器,然后单击底部的“高级”选项卡。

在这里,单击“添加”按钮,然后选择“从项目输出添加程序集…”以显示对话框。从下拉列表中选择类库项目,注意 Location
文本框已填充了程序集名称。接下来,单击“安全控件”网格中的“单击此处添加新项”按钮,然后填写适当的设置。单击“确定”按钮返回到上一个屏幕,您会注意到信息已添加到“其他程序集”网格中。再次,确保 SharePoint 项目不是沙盒解决方案,否则无论这些设置如何,程序集都不会部署到 GAC。

现在已将程序集添加到部署包中,我们可以继续进行编码。要添加必要的 MenuItemTemplate
和 SubMenuTemplates
来重新创建前面的示例,您需要重写 CreateChildControls
方法并添加如下所示的代码:
protected override void CreateChildControls()
{
SubMenuTemplate sub = new SubMenuTemplate();
sub.Text = "Custom Menu";
sub.Description = "Custom menu with submenu";
sub.ImageUrl = "/_layouts/images/settingsIcon.png";
sub.MenuGroupId = 90;
sub.Sequence = 100;
Controls.Add(sub);
MenuItemTemplate item = new MenuItemTemplate("SubMenu");
item.Description = "A submenu item";
item.ImageUrl = "/_layouts/images/settingsIcon.png";
sub.Controls.Add(item);
SubMenuTemplate sub2 = new SubMenuTemplate();
sub2.Text = "Custom Menu";
sub2.Description = "Custom menu with submenu";
sub2.ImageUrl = "/_layouts/images/settingsIcon.png";
sub.Controls.Add(sub2);
item = new MenuItemTemplate("SubMenu");
item.Description = "A submenu item";
item.ImageUrl = "/_layouts/images/settingsIcon.png";
sub2.Controls.Add(item);
}
这里没有什么令人惊讶或困难的,这只是创建控件并按正确的顺序将它们添加到正确的 Controls
集合的问题。第一部分提到的属性仍然可以应用于此处,为简单起见,没有设置任何 OnClientClickXXX
属性。
从 Elements.xml 创建菜单
尽管从代码创建菜单相对简单易懂,但有些人可能更喜欢使用更声明式的方法,并在 Elements.xml 文件中定义菜单结构。但是,这仍然需要代码来将菜单项添加到“站点操作”菜单。首先,向类库项目添加另一个类,该类再次派生自 WebControl
并实现 CreateChildControls
方法。然后,向 SharePoint 项目添加另一个模块。像以前一样删除 Sample.txt 文件,并将上一个 Elements.xml 文件的内容复制到刚刚创建的文件中,并将 ControlClass
属性更改为匹配新创建的类。
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
<customaction
id="MANSoftDev_SiteAction"
sequence="1"
controlclass="MANSoftDev.SiteAction.SiteActionDeclarative"
controlassembly="MANSoftDev.SiteAction, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=2e15c7b1150d656d"
location="Microsoft.SharePoint.StandardMenu"
groupid="SiteActions">
</customaction>
</elements>
现在您有了一个起点,您可以开始在 Elements.xml 文件中将菜单项添加为 CustomAction 元素。重要的属性是 Location
和 GroupId
。这些将在代码中使用来创建与菜单组对应的 FeatureMenuTemplate
。
<elements xmlns="http://schemas.microsoft.com/sharepoint/">
<customaction
id="MANSoftDev_SiteAction"
sequence="1"
controlclass="MANSoftDev.SiteAction.SiteActionDeclarative"
controlassembly="MANSoftDev.SiteAction, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=2e15c7b1150d656d"
location="Microsoft.SharePoint.StandardMenu"
groupid="SiteActions">
</customaction>
<customaction
id="subMenu"
title="SubMenu"
description="A submenu item" sequence="100"
imageurl="/_layouts/images/settingsIcon.png"
location="MANSoftDev.CustomMenu"
groupid="MANSoftDev_Sub1">
</customaction>
<customaction
id="subMenu"
title="SubMenu"
description="A submenu item"
sequence="100"
imageurl="/_layouts/images/settingsIcon.png"
location="MANSoftDev.CustomMenu"
groupid="MANSoftDev_Sub2">
</customaction>
</elements>
在 ControlClass
中,您将再次重写 CreateChildControls
方法来创建与上面所示的 CustomAction
元素对应的 SubMenuTemplates
和 FeatureMenuTemplates
。
protected override void CreateChildControls()
{
// Initialize collections
SubMenus = new List();
FeatureMenus = new Dictionary();
// Create the SUbMenuTemplates
SubMenuTemplate submenu = CreateSubMenu();
Controls.Add(submenu);
submenu = CreateSubMenu();
Controls.Add(submenu);
// Create the FeatureMenuTemplates
FeatureMenuTemplate featureMenu = CreateFeatureMenu("MANSoftDev_Sub1");
Controls.Add(featureMenu);
featureMenu = featureMenu = CreateFeatureMenu("MANSoftDev_Sub2");
Controls.Add(featureMenu);
}
private FeatureMenuTemplate CreateFeatureMenu(string groupId)
{
FeatureMenuTemplate featureMenu = new FeatureMenuTemplate();
featureMenu.Location = "MANSoftDev.CustomMenu";
featureMenu.GroupId = groupId;
FeatureMenus.Add(groupId, featureMenu);
return featureMenu;
}
CreateSubMenu
方法很简单,并且与前面使用的基于代码的方法类似。但是,CreateFeatureMenu
方法是通过 GroupId
属性将 CustomAction
元素链接到 FeatureMenuTemplate
的地方。如果此时调试代码,您会发现 FeatureMenuTemplates
中没有子项;但是,在 OnPreRender
方法中,它们有。魔力发生在 FeatureMenuTemplate
控件的 CreateChildControls
方法中。在这里,SharePoint 将使用 internal
类 SPElementProvider
来提取 CustomAction
元素,并根据 GroupId
进行匹配,然后创建添加到 Controls
集合的 MenuItemTemplates
。
...
SPElementProvider availableProvider = SPElementProvider.GetAvailableProvider();
...
if (element2.Location != "CommandUI.Ribbon.Custom")
{
MenuItemTemplate menuItem = new MenuItemTemplate();
menuItem.Description = element2.Description;
menuItem.ClientOnClickNavigateUrl =
SPCustomActionElement.ServerRelativeUrlFromTokenizedUrl
(element2.UrlAction, web, list, null);
menuItem.Text = element2.Title;
menuItem.Sequence = element2.Sequence;
menuItem.MenuGroupId = element2.MenuGroupId;
menuItem.ImageUrl = element2.ServerRelativeImageUrl;
this.OnAddMenuItem(new AddMenuItemEventArgs(menuItem));
if (menuItem.Visible)
{
this.Controls.Add(menuItem);
}
...
在 OnPreRender
事件期间,您将遍历 FeatureMenuTemplates
并将 MenuItemTemplates
移动到适当的 SubMenuTemplate
,并将第二个级别的 SubMenuTemplate
添加到父级别。
protected override void OnPreRender(EventArgs e)
{
// Add the first level menu items
while(FeatureMenus["MANSoftDev_Sub1"].Controls.Count != 0)
{
MenuItemTemplate menu = FeatureMenus
["MANSoftDev_Sub1"].Controls[0] as MenuItemTemplate;
SubMenus[0].Controls.Add(menu);
}
// Add the second level menu items
while(FeatureMenus["MANSoftDev_Sub2"].Controls.Count != 0)
{
MenuItemTemplate menu = FeatureMenus
["MANSoftDev_Sub2"].Controls[0] as MenuItemTemplate;
SubMenus[1].Controls.Add(menu);
}
// Add the second level submenu to the top
SubMenus[0].Controls.Add(SubMenus[1]);
base.OnPreRender(e);
}
如您所见,此方法比前几种方法稍复杂,并且需要 Elements.xml 和代码之间的协调。但是,它可以让一个团队成员定义菜单,而另一个团队成员编写行为,或者创建一个可以在多个项目中重用的方法。
结论
我介绍了三种用于 SharePoint 2010 构建和部署带有子菜单的 SiteAction 菜单项的方法。不能说哪种方法“更好”,选择哪种方法将取决于您的需求。本文旨在介绍不同的技术,并为您提供选择最适合您项目的知识。
历史
- 首次发布:2010 年 11 月 14 日