自定义和扩展 SharePoint 2010 Server 功能区

了解构成 SharePoint 2010 服务器功能区的组件以及如何通过演练两个功能区自定义项方案来自定义功能区。

适用范围: Microsoft SharePoint Foundation 2010 | Microsoft SharePoint Server 2010 | Microsoft Visual Studio 2010 中的 SharePoint 开发工具

供稿人:Andrew Connell,Critical Path Training, LLC(该链接可能指向英文页面)

目录

单击以获取代码  下载本文附带的代码:MSDN 示例 - 自定义和扩展 SharePoint 2010 Server 功能区(该链接可能指向英文页面)

SharePoint 2010 Server 功能区简介

随着 2007 Microsoft Office system 的发布,Microsoft 引入了一个称作“功能区”的新用户界面结构,它代替了以前的应用程序下拉菜单和工具栏。大量的调查研究表明,某些应用程序(例如,Microsoft Word)包含太多命令,导致用户难以找到所需的命令,功能区正是为解决这个问题而引入的。

功能区的前提是提供一个更加以结果为导向的界面。用户可将精力集中在他们执行的操作上,系统将仅显示用户可在特定时间执行的操作的命令。例如,当用户明确地键入文本并且未选择图像或表时,没有理由为用户提供与文档中的表或图像交互的命令。但是,通过使用“插入”选项卡,用户可轻松添加表或图片。仅在用户选择了表或图片时,才会出现新的命令结构。当表或图片不再是焦点时,这些命令将消失。

在 2007 Microsoft Office system 中,已在 Microsoft Word、Microsoft Excel 和 Microsoft PowerPoint 中实现功能区。Microsoft 在此基础上进行了扩展,并已将功能区添加到所有 Microsoft Office 2010 应用程序(包括 Microsoft OneNote 2010、Microsoft Access 2010 和 Microsoft InfoPath 2010)。

类似于 Microsoft Office 客户端,SharePoint 用户在查找控件以完成其工作上遇到了类似的难题。命令在页面中的多个位置存在,从“网站操作”菜单到管理 Web 部件或到列表中的“编辑控件块”(ECB) 菜单。图 1 演示了对 Windows SharePoint Services 3.0 网站中的用户可用的所有菜单。



图 1. Windows SharePoint Services 3.0 中的菜单和命令

Windows SharePoint Services 网站菜单

在 Microsoft SharePoint 2010 中,Microsoft 已将服务器功能区添加到 SharePoint,以解决用户在使用 Microsoft Office 客户端中的许多命令时遇到的难题。已将所有命令和菜单项从 SharePoint 中的主工作区向上推入始终固定到浏览器窗口顶部的功能区。2007 Microsoft Office system 的用户或已实现功能区的其他应用程序的用户会发现,由于 SharePoint 功能区与 Office 客户端功能区非常相似,因此调节 SharePoint 功能区非常轻松。该功能区在外观、工作方式和执行的操作上与 Office 功能区类似,并且具有与 Office 功能区相同的控件。Office 功能区和 SharePoint 服务器功能区之间的唯一差异与技术有关;例如,与在桌面(胖客户端)体验中一样,难以在 Web(瘦客户端)体验中显示不同的字体。

SharePoint 2010 中的功能区是基于与 Office 客户端功能区类似的体系结构构建的。Microsoft 使用此体系结构在 SharePoint 2010 中实现默认功能区。此体系结构还允许第三方开发人员自定义和扩展 SharePoint 中包含的现有功能区组件以及创建命令结构。

本文介绍了 SharePoint 服务器功能区的组件,以便您了解功能区的构造方式。然后,说明并演示了开发人员自定义和扩展功能区的方式。SharePoint Foundation 2010 通用参考中有一节专门介绍了功能区,并包含各种自定义演练。在自定义功能区时,建议您将本文与 SharePoint Foundation 2010 通用参考(具体而言是 SharePoint Foundation 中的服务器功能区一节)配合使用。

SharePoint 2010 Server 功能区体系结构

默认功能区由 SharePoint 构建且基于作为主安装的一部分的单个文件。此文件是在路径 {SharePoint Root}\TEMPLATE\GLOBAL\XML\CMDUI.XML 中找到的全局网站定义的一部分。此文件包含 SharePoint Foundation 2010 中的功能区组件(例如,“浏览”、“页”、“列表”、“库”和“文档”选项卡)的定义。

虽然 CMDUI.xml 文件包含所有核心服务器功能区组件,这与对功能区实现其他自定义的方式无关。SharePoint 功能用于实现对功能区的其他更改。例如,Microsoft SharePoint Server 2010 包括许多需要功能区修改的任务,例如,与“企业内容管理”、“表单服务”和商业智能相关的任务。所有这些更改都是通过使用 SharePoint 功能实现的。SharePoint 开发人员还可通过使用功能来自定义功能区,如本文后面的创建自定义 SharePoint 2010 Server 功能区组件中所述。

SharePoint 2010 扩展了功能架构(具体而言为 <CustomAction /> 元素),使其成为所有功能区自定义项的工具。通过将 Location 属性设置为 CommandUI.Ribbon 并添加一个 <CommandUIExtension /> 子元素,可以做到这一点。SharePoint 2010 SDK 概述了服务器功能区 XML

以下各节说明了功能区的两大核心部分:构成视觉体验的组件以及在单击功能区中某个控件时运行的代码。

SharePoint 2010 Server 功能区组件

SharePoint 2010 服务器功能区包含各种组件,如图 2 所示。



图 2. SharePoint 2010 Server 功能区组件

SharePoint 2010 服务器功能区组件

图中的标注号指向以下特定组件:

  1. 选项卡



  2. 控件

  3. 上下文选项卡组

后面几节简要介绍了这些组件及其使用。

SharePoint 2010 Server 功能区上的选项卡

选项卡是服务器功能区的根。它们包含一个或多个组,并包含相似的功能。例如,在图 2 中,当前选定的“页”选项卡包含与使用当前页相关的功能。

SharePoint 2010 Server 功能区上的上下文选项卡组

上下文选项卡组用于提供当前上下文的非全局功能(例如页)。它们仅在遇到某些情况时出现且包含一个或多个选项卡。

例如,图 2 中的功能区演示“库工具”上下文选项卡组,该组仅在文档库中显示或(在此示例中)当选中与文档库关联的当前页面上的“列表视图 Web 部件”时显示。

上下文选项卡组会在功能和菜单选项不可用时为用户隐藏它们,并在功能和菜单选项可用时为用户显示它们。上下文选项卡组的其他示例包括在编辑 wiki 页时出现的“编辑工具”上下文选项卡组,或在编辑模式下选中某张图片时出现的“图片工具”上下文选项卡组。

SharePoint 2010 Server 功能区上的组

功能区中的每个选项卡均包含一个组或一系列组。组用于将控件与相似功能关联。每个组均与一个模板关联,该模板根据功能区缩放定义组的布局及其显示方式。功能区缩放是指功能区中有太多控件要显示的情况,例如,当浏览器未处于全屏最大化显示状态而只是处于窗口状态时。

SharePoint 2010 Server 功能区上的组模板

组模板用于为组中的控件定义不同的布局选项。Microsoft 在 CMDUI.xml 文件中包含 29 个组模板(若要查找这些模板,请在此文件的结尾搜索元素 <RibbonTemplates />)。

SharePoint 2010 Server 功能区上的控件

如果用户未选择或单击任何项,则功能区将是不完整的。控件是用户可与之交互的功能区内部的项。控件驻留在组中,其中包括按钮、切换按钮、复选框、文本框和许多其他控件。有关所有可用控件的完整列表,请参阅服务器功能区的体系结构

每个控件定义包含一个 Command 属性,在单击或选中该属性时,会告知功能区基础结构要执行的操作。

SharePoint 2010 中的服务器功能区命令

服务器功能区使用命令处理用户执行的单击或选择操作。熟悉命令基础结构在 Windows Presentation Foundation 或 Microsoft Silverlight 4 中的工作方式的用户会发现它等同于服务器功能区命令基础结构。每个命令都进行了命名,并在控件中引用相应的名称。最重要的是,这些命令包含两条非常重要的详细信息:

  • 当前命令是否可用  例如,“删除”命令仅在文档库中或选中文档时可用。

  • 应运行的代码  例如,“删除”命令可能会使用 SharePoint 2010 客户端对象模型来删除列表中的项、显示已删除文档通知并在页面上刷新列表视图 Web 部件。

主题服务器功能区的体系结构包含与命令基础结构的工作方式相关的其他详细信息。

通过使用 ECMAScript(JavaScript、JScript) 编写服务器功能区中的命令。可通过两种方式实现命令:

  • 通过命令 UI 处理程序

  • 通过页面组件

以下各节中对这两个选项进行了说明,并讨论了每个选项的优缺点。通常,简单且相对较小的命令适用于命令 UI 处理程序选项。但是,较为复杂且需要实现大量 JavaScript 的命令可能更适用于页面组件。本文后面的文档库示例中使用的示例功能区自定义项演示如何使用每种技术来执行完全相同的操作。

使用命令 UI 处理程序实现服务器功能区命令

命令 UI 处理程序是通过结合使用声明性标记和 JavaScript 来实现的。在定义功能区自定义项(使用 <CommandUIExtension /> 元素进行定义)的同一 Feature 元素清单中使用 <CommandUIHandler /> 元素定义这些处理程序。此元素包含以下三个属性:

  • Command  将在控件中引用的命令名称。

  • CommandAction  触发该命令时执行的 JavaScript。

  • EnabledScript  JavaScript,服务器功能区命令基础结构调用它来确定该命令是否可用。此脚本应返回一个布尔值,如果该命令可用,则为 TRUE;如果该命令不可用,则为 FALSE。如果该命令不可用,则功能区基础结构会将功能区中的该命令灰显,并且在用户选择该命令时不调用它。

使用命令 UI 处理程序的优点

通常,对于大多数开发人员而言,命令 UI 处理程序更易于编写和管理。由于这些处理程序是以声明方式定义的,因此功能区框架会将脚本添加到其中的引用控件需要命令的任何页面。

使用命令 UI 处理程序的缺点

命令 UI 处理程序的缺点之一是,当它们包含大量自定义 JavaScript 时会变得难以管理、调试和进行故障排除。此外,由于这些处理程序会作为内联脚本块添加到每个页面中,因此无法通过浏览器对其进行缓存,并且每次都必须下载,从而增加了整体页权重。

使用页面组件实现服务器功能区命令

除了使用命令 UI 处理程序外,还可以使用页面组件。页面组件是一个在外部脚本库 (.js) 文件中定义的 JavaScript 对象。该对象实现了几个属性和方法,它们将告知服务器功能区命令基础结构如何初始化该对象、该对象可处理哪些命令以及某个特定命令是否可用,并可在页面组件接收焦点或失去焦点时做出响应。

必须将此脚本文件添加到显示功能区自定义项的同一页面中。可通过多种方法来实现这一点。第一种方法是,使用 SharePoint 2010 中的新 <CustomAction ScriptSrc="" /> 功能,根据功能的范围将库添加到网站、网站集、Web 应用程序或服务器场中的所有页面。另一种方法是,从自定义应用程序或网站页面 (.aspx)、自定义用户控件 (.ascx) 或自定义服务器控件内的托管代码添加脚本。以下代码示例将向 Web 部件内的页面添加页面组件文件。

C#
private void LoadAndActivateRibbonContextualTab() {
  SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);
  // Ensure ribbon exists.
  if (ribbon != null) {
    // Load dependencies if not already on the page.
    ScriptLink.RegisterScriptAfterUI(this.Page, "SP.Ribbon.js", false, true);

    // Load and activate contextual tab.
    ribbon.MakeTabAvailable("Ribbon.PropertyChangerTab");
    ribbon.MakeContextualGroupInitiallyVisible("Ribbon.WebPartContextualTabGroup", string.Empty);
  }
}

使用页面组件的优点

由于所有 JavaScript 都位于外部脚本库中,因此更易于对页面组件(而非命令 UI 处理程序)进行管理、故障排除和调试(Microsoft Visual Studio 2010 脚本调试器可设置断点并附加到库)。另外,由于它是一个外部库,因此浏览器可缓存该库,而无需每次在页面上引用它时向它发出请求。

还可利用页面组件更好地控制命令,因为它们可通过接收或丢失焦点来启用或禁用页面组件,并处理上述每种情况下发生的事件。

另外,由于它们是外部库,因此,一个页面组件可处理多个控件的命令,并可在各种功能区自定义项中重用。

使用页面组件的缺点

相对于使用命令 UI 处理程序,使用页面组件的一个缺点是需要大量脚本。这主要是因为开发人员在页面上构建、注册和初始化一个脚本对象。另外,不熟悉面向对象的 JavaScript 技术的开发人员会发现要做到这一点会有点难度。

使用页面组件的另一个缺点是,必须将其添加到页面,因为这不是由功能区命令基础结构处理的。

自定义 SharePoint 2010 Server 功能区

当开发人员希望自定义 SharePoint 服务器功能区时,会为其提供两个选项。以声明方式或以编程方式应用自定义项。使用功能实现本文前面所述的声明性方法;具体而言,带 <CommandUIExtensions /> 子元素的 <CustomAction /> 元素。

编程方法包括将声明性构造作为字符串添加到服务器功能区对象中。本文的其余内容将重点说明声明性方法,因为也可将这些技术应用于编程方法。SharePoint 2010 SDK 包含有关以编程方式自定义功能区的演练:演练:使用上下文选项卡创建自定义 Web 部件

自定义内置 SharePoint 2010 Server 功能区组件

开发人员可通过三种方式自定义功能区:

  • 向功能区添加组件(选项卡、上下文选项卡组、组和控件)。

  • 在功能区中修改现有组件。

  • 从功能区中删除现有组件。

可以声明方式执行所有这三个选项。服务器功能区 XML 演示了不同的元素。服务器功能区的声明性自定义项演示如何向功能区添加三个常用组件。

修改和移除组件(例如,选项卡、组和控件)的方式与修改和移除相同架构元素的方式很类似。通过重复元素的 ID 并替换其内容来修改元素,这与内容占位符在 Microsoft ASP.NET 2.0 母版页中的工作方式类似。移除操作与修改操作相同,只不过容器保留为空。有关详细信息,请参阅服务器功能区的声明性自定义项

创建自定义 SharePoint 2010 Server 功能区组件

以下两节演示如何自定义 SharePoint 2010 服务器功能区。这两个演练都使用了各种技术,并对每个组件进行了详细说明。有关这两个演练的示例代码,请参阅 MSDN 示例 - 自定义和扩展 SharePoint 2010 Server 功能区(该链接可能指向英文页面)。此示例代码只需要一个 SharePoint Foundation 2010 网站集。

第一个示例为 WebPartRibbonContextualTab(如图 3 所示),它演示如何创建使用以下功能区自定义项的自定义 Web 部件:

  • 上下文选项卡组

  • 自定义选项卡

  • 自定义组

  • 命令 UI 处理程序

  • 仅当 Web 部件位于页面上时激活上下文选项卡组

  • 单击功能区上的按钮时触发并处理服务器端回发。



图 3. 带上下文选项卡组的 Web 部件

具有上下文选项卡组的 Web 部件

第二个示例为 ApplyDocumentPrefixRibbon(如图 4 所示),它演示如何实现以下自定义项和技术:

  • 现有功能区选项卡中的自定义组。

  • 具有不同布局选项的自定义组模板。

  • 执行相同操作的两个自定义命令,只不过其中一个命令是通过命令 UI 处理程序实现的,而另一个命令实现自定义页面组件:

    • 除非选中库中的一个或多个文档,否则将禁用命令。

    • 单击时,命令将打开一个对话框,传递选定文档并根据用户输入执行操作。

    • 在对话框的工作完成后,该对话框将关闭,同时命令将显示一条通知消息并刷新列表视图 Web 部件而非整个页面。

  • 按条件加载页面组件的自定义服务器控件。



图 4. 具有页面组件的自定义组

具有页面组件的自定义组
Gg552606.note(zh-cn,office.14).gif注释:

为了便于阅读,已略去示例代码中的某些标记。有关完整代码引用,请参阅 MSDN 示例 - 自定义和扩展 SharePoint 2010 Server 功能区(该链接可能指向英文页面)

如何:创建支持 Web 部件的上下文选项卡并利用回发

在本示例中,在将 Web 部件添加到页面时,将显示上下文选项卡,如之前的图 3 所示。此选项卡包含提供几个按钮的两个组。第一个组中的按钮不执行任何操作,而只演示如何使用默认模板之一。但是,“Write to Web Part via PostBack”按钮会发布一次回发。Web 部件包含用于检查按钮是否已发布回发的代码,如果出现此情况,则向 Web 部件的内容中添加一些文本。

步骤 1:创建 Web 部件

第一个步骤是创建 Web 部件。使用新的 Microsoft Visual Studio 2010 中的 SharePoint 开发工具创建一个 SharePoint 项目,并向该项目中添加一个 Web 部件 SharePoint 项目项。CreateChildControls 方法执行两个操作:将一些文本写入 Web 部件,并在引发特定回发事件时调用方法以处理情况,如以下代码中所示。

C#
public class RibbonizedWebPart : WebPart {
  private string POSTBACK_EVENT = "RibbonizedWebPartPostback";

  protected override void CreateChildControls() {
    this.Controls.Add(
      new LiteralControl(
        "<em>Ribbonized Web Part contents go here</em>"
      )
    );

    // Handle postback from ribbon. 
    HandleRibbonPostback();
  }
}

接下来,创建处理回发事件的方法。如果它是一个特定的回发事件,则它会将其他一些文本写入 Web 部件的内容,如以下代码所示。

C#
private void HandleRibbonPostback() {
  if (this.Page.Request["__EVENTTARGET"] == POSTBACK_EVENT) {
    this.Controls.Add(
      new LiteralControl(
        "<p>Responding to postback event from ribbon.</p>"
      )
    );
  }
}

最后一步是,实现将向页面中添加自定义上下文选项卡的代码。可从 Web 部件的生命周期的 OnPreRender 阶段实现这一点。它必须获得对服务器功能区的引用,并确保已在页面上加载脚本依赖项。最后,为了显示上下文选项卡,它使 ID 为 Ribbon.PropertyChangerTab 的选项卡可用,并告知功能区在加载页面时使 ID 为 Ribbon.WebPartContextualTabGroup 的上下文选项卡可用,如以下代码所示。

C#
protected override void OnPreRender(EventArgs e) {
  LoadAndActivateRibbonContextualTab();
  base.OnPreRender(e);
}

private void LoadAndActivateRibbonContextualTab() {
  SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);
  // Ensure ribbon exists.
  if (ribbon != null) {
    // Load dependencies if not already on the page.
    ScriptLink.RegisterScriptAfterUI(this.Page, 
      "SP.Ribbon.js", false, true);

    // Load and activate contextual tab.
    ribbon.MakeTabAvailable("Ribbon.PropertyChangerTab");
    ribbon.MakeContextualGroupInitiallyVisible(
      "Ribbon.WebPartContextualTabGroup", string.Empty);
  }
}

步骤 2:创建服务器功能区组件自定义项

创建 Web 部件后,下一步是创建服务器功能区组件自定义项。为此,您创建一个上下文选项卡组,一个带两个组的选项卡和几个控件。

Microsoft Visual Studio 2010 中的 SharePoint 开发工具不包含用于自定义功能区的 SharePoint 项目项模板。然而,通用的 SharePoint 项目项“空元素”可用于包含功能区自定义项,因此向项目中添加一个新的空元素。

步骤 2.1:向 Element 中添加核心功能区标记

在项目的新“Element”SharePoint 项目项中,向 element.xml 文件中添加以下标记。

XML
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="WebPartContextualTabs" Location="CommandUI.Ribbon">
    <CommandUIExtension>
      <CommandUIDefinitions />
      <CommandUIHandlers />
    </CommandUIExtension>
  </CustomAction>
</Elements>

这将告知 SharePoint 以下内容:

  • 元素清单包含适用于任何位置的功能区 (<CustomAction Location="CommandUI.Ribbon" />) 的功能区自定义项。有五个针对 Location 属性的选项。开发人员可以通过其他选项指定,在显示列表视图 Web 部件时或者在某个项的显示、新建或编辑表单上应显示自定义项。另外,RegistrationTypeRegistrationId 允许开发人员对自定义项的显示位置进行更精确地定位。例如,开发人员可面向特定的内容类型。服务器功能区 XML 中列出了所有选项。

  • 还包括功能区自定义项的基本结构。

步骤 2.2:添加新的上下文选项卡组

接下来,添加将创建上下文选项卡组的以下标记。

XML
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="WebPartContextualTabs" Location="CommandUI.Ribbon">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition 
                Location="Ribbon.ContextualTabs._children">
          <ContextualGroup Id="Ribbon.WebPartContextualTabGroup"
                ContextualGroupId="WebPartContextualTab"
                Title="Ribbonized Web Part Tools"
                Sequence="150"
                Color="Green"
                Command="WebPartContextualTab.OnEnableContextualTab">
          </ContextualGroup>
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers />
    </CommandUIExtension>
  </CustomAction>
</Elements>

<CommandUIDefinition> Location 属性告知功能区向其中的上下文选项卡添加以下内容,即一个或多个 <ContextualGroup /> 元素。

<ContextualGroup /> 元素定义新组。它具有一个与上下文组的名称匹配的 Id 属性,Web 部件的代码告知功能区在最初加载页面时使该上下文组可见。

Sequence 属性告知 SharePoint 此上下文组的顺序(当存在其他上下文组时)。为 SharePoint 上下文组给定的序列将从 100 开始(即“编辑工具”上下文组),然后按 100 递增。因此,如果存在“编辑工具”组,则上下文组可能会显示在第一个或第二个位置。有关如何查找其他上下文组的名称和序列的信息,请参见本文后面的自定义 SharePoint 2010 Server 功能区开发提示和技巧一节。

Command 属性将此组与命令名称关联。在某些情况下,当选中此选项卡组时,运行脚本可能会很有用。但在此情况下,命令(稍后实现)用于告知功能区激活上下文组的时间。如果未实现,则一旦用户单击任何项,就会促使功能区隐藏自定义组。

步骤 2.3:向上下文选项卡组添加选项卡

在创建上下文选项卡组后,添加以下标记以添加新选项卡并定义最大大小布局组模板。

XML
<CommandUIDefinitions>
  <CommandUIDefinition Location="Ribbon.ContextualTabs._children">
    <ContextualGroup Id="Ribbon.WebPartContextualTabGroup" ... >
      <Tab Id="Ribbon.PropertyChangerTab" 
           Title="Tools" Sequence="501">
        <Scaling Id="Ribbon.PropertyChangerTab.Scaling">
          <MaxSize Id="Ribbon.PropertyChangerTab.MaxSize"
             GroupId="Ribbon.PropertyChangerTab.PropertyGroup"
             Size="LargeLarge" />
          <MaxSize Id="Ribbon.PropertyChangerTab.MaxSize" ... />
        </Scaling>
      </Tab>
    </ContextualGroup>
  </CommandUIDefinition>
</CommandUIDefinitions>

在围绕 <MaxSize /> 元素的此代码段中,需要注意两点。它将稍后定义的组(通过 GroupId 属性)与模板中定义的布局选项 LargeLarge(通过 Size 属性)关联。

步骤 2.4:向选项卡添加组

现在,通过添加以下标记向新选项卡添加组。

XML
<CommandUIDefinitions>
  <CommandUIDefinition Location="Ribbon.ContextualTabs._children">
    <ContextualGroup Id="Ribbon.WebPartContextualTabGroup" ... >
      <Tab Id="Ribbon.PropertyChangerTab" ... >
        <Scaling Id="Ribbon.PropertyChangerTab.Scaling" ... >
        </Scaling>
        <Groups Id="Ribbon.PropertyChangerTab.Groups">
          <Group Id="Ribbon.PropertyChangerTab.PropertyGroup" ... >
            ...
          </Group>
          <Group Id="Ribbon.PropertyChangerTab.PostBackGroup"
                 Title="PostBack" Sequence="25"
                 Template="Ribbon.Templates.Flexible2">
          </Group>
        </Groups>
      </Tab>
    </ContextualGroup>
  </CommandUIDefinition>
</CommandUIDefinitions>

虽然本示例中有两个组,但此代码列表中仅显示第二个组。请注意,组包含 Id 属性,该属性与之前已添加的 <MaxSize GroupId="" /> 元素匹配。

另请注意,组使用的是一个名为 Ribbon.Templates.Flexible2 的模板。该模板包含在内置 CMDUI.xml 功能区声明中。在功能区自定义项功能中定义自定义组时,Microsoft 建议开发人员创建其自己的组模板,而不是使用提供的组模板之一。这样做的原因是,SharePoint 不会为每个页面请求加载整个功能区;而只加载当前上下文所需的部分功能区。这包括组模板。因此,如果开发人员依赖内置组模板,则可以不为当前页面加载模板且不显示其功能区自定义项。

对于此示例自定义项,由于这是 Web 部件的一部分,并且 Web 部件必须位于 Web 部件页上,因此,假定将加载此模板,因为它是每个 Web 部件页上都具有的“页面”选项卡的一部分。

有关如何查找其他模板的名称的信息,请参阅本文后面的自定义 SharePoint 2010 Server 功能区开发提示和技巧

步骤 2.5:向组添加控件

定义功能区自定义项的可见部分的最后一个步骤是向组添加控件。将以下标记添加到第二个组(请参阅 MSDN 示例 - 自定义和扩展 SharePoint 2010 Server 功能区(该链接可能指向英文页面),以将按钮添加到第一个组)。

XML
<CommandUIDefinitions>
  <CommandUIDefinition Location="Ribbon.ContextualTabs._children">
    <ContextualGroup Id="Ribbon.WebPartContextualTabGroup" ... >
      <Tab Id="Ribbon.PropertyChangerTab" ... >
        <Scaling Id="Ribbon.PropertyChangerTab.Scaling" ... >
        </Scaling>
        <Groups Id="Ribbon.PropertyChangerTab.Groups">
          <Group Id="Ribbon.PropertyChangerTab.PropertyGroup" ... >
            ...
          </Group>
          <Group Id="Ribbon.PropertyChangerTab.PostBackGroup" ... >
            <Controls Id="Ribbon.PropertyChangerTab.PropertyGroup.Controls">
              <Button Id="Ribbon.PropertyChangerTab.PropertyGroup.GeneralDialogButton"
                      LabelText="Write to Web Part"
                      Command="WebPartContextualTabs.OnPostback"
                      TemplateAlias="o1"
                      Sequence="15"
                      Image16by16="/_layouts/Images/WebPartRibbonContextualTab/16x16Placeholder.png"
                      Image32by32="/_layouts/Images/WebPartRibbonContextualTab/32x32Placeholder.png" />
            </Controls>
          </Group>
        </Groups>
      </Tab>
    </ContextualGroup>
  </CommandUIDefinition>
</CommandUIDefinitions>

此标记将向具有写入 Web 部件的标签的组中添加一个新按钮控件。在单击此按钮时,它将触发 Command=""TemplateAlias 属性告知服务器功能区定义的组模板将按钮放置到的位置。最后,布局将使用两个图像属性来显示处于不同状态的功能区图标。这些图像可指向服务器上的任何图像。在此示例中,它们指向已添加到项目中的图像。

步骤 3:创建服务器功能区命令

最后一个步骤是,定义已在功能区组件中引用的两个命令。将以下标记添加到元素清单。

XML
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="WebPartContextualTabs" Location="CommandUI.Ribbon">
    <CommandUIExtension>
      <CommandUIDefinitions>
        ...
      </CommandUIDefinitions>
      <CommandUIHandlers>
        <CommandUIHandler Command="WebPartContextualTab.OnEnableContextualTab"
          CommandAction=""
          EnabledScript="return true;" />
        <CommandUIHandler Command="WebPartContextualTabs.OnPostback"
          CommandAction="javascript:__doPostBack('RibbonizedWebPartPostback','');" />
      </CommandUIHandlers>
    </CommandUIExtension>
  </CustomAction>
</Elements>

第一个 <CommandUIHandler /> 具有一个与上下文选项卡组关联的命令。如前所述,此命令仅用来告知 Server 功能区其激活时间,而不执行任何其他操作。为了在功能区中不显示此命令,其 EnabledScript 属性将始终返回 TRUE。因此,如果上下文选项卡组位于页面上(此时 Web 部件也位于页面上,因为 Web 部件将处理其激活),则它决不会消失。

第二个 <CommandUIHandler /> 与按钮控件关联。当单击该控件时,将触发一个包含名为 RibbonizedWebPartPostBack 的事件的回发。回想一下,Web 部件中的代码正在侦听该事件名称,并在发现该名称时向 Web 部件中添加文本。

步骤 4:部署和测试

在保存所有更改后,通过在 Visual Studio 2010 中按 F5,或通过在“调试”菜单上单击“开始调试”来部署自定义 Web 部件。

在加载初始网页时,您会发现未显示上下文选项卡,如图 5 所示。



图 5. 不具有上下文选项卡组的调试网站的主页

不含上下文选项卡的调试网站主页

现在,通过选择“页面”选项卡,然后单击“编辑”,可将页面置于编辑模式中。将页面上的 Web 部件插入默认位置。在重新加载页面时,您会发现,上下文选项卡和 Web 部件都是可见的,如图 6 所示。



图 6. 上下文选项卡和 Web 部件是可见的

显示上下文选项卡 Web 部件

通过选择自定义上下文选项卡组中的“工具”选项卡,然后单击相应的按钮来测试回发。页面应被刷新并在 Web 部件中包含其他文本,如图 7 所示。



图 7. 响应来自功能区的回发的 Web 部件

用于响应功能区回发的 Web 部件

还请注意,上下文选项卡组将作为第二个上下文选项卡组显示在页面上。这是因为,页面仍处于编辑模式中,并且序列已设置为 150(大于编辑工具序列)。在页面退出编辑模式后,自定义上下文组应显示在第一个位置。

演练:向文档选项卡中添加按钮并有条件地启用它们

本演练中的示例演示如何将带按钮的新组添加到现有功能区。在部署项目时,它会在“文档”选项卡中注册一个新组,该组是“库工具”上下文选项卡组的一部分。此上下文选项卡组仅在用户与文档交互或位于文档库中时显示。图 8 演示了不带功能区的工作组网站主页,但在选中共享文档库时(如图 9 所示),显示的上下文选项卡组会带自定义项。



图 8. 未选定文档库的工作组网站主页

没有选择文档库的工作组网站页面

图 9. 已选定文档库的工作组网站主页

选择了文档库的工作组网站页面

最初,三个按钮中有两个按钮未启用。除非选定一个或多个文档,否则将启用与按钮关联的命令,如图 10 所示。



图 10. 在选定文档时启用的功能区按钮

选择文档时启用功能区按钮

在单击后面两个按钮(即“Apply Prefix w/ CommandUI Handler”和“Apply Prefix w/ Page Component”)时,将执行相同的操作。但从其名称可了解,一个按钮将使用命令 UI 处理程序,而另一个按钮将使用自定义页面组件。在单击按钮时,它们会收集页面上所有选定文档的列表,并将这些文档传递给一个新对话框,如图 11 所示。此对话框允许用户向所有选定文档的名称添加前缀。



图 11. 从功能区自定义项引发的自定义对话框

从功能区自定义项引发的自定义对话框

在单击“Set Prefix”按钮时,对话框将更新所有文档的名称,然后关闭。根据对话框是通过单击“Set Prefix”按钮还是通过单击“Cancel”来关闭的,命令会发出一条通知消息,然后刷新页面上的列表视图 Web 部件,以更新列表内容。

步骤 1:创建服务器功能区组件自定义项

第一步是,使用 Microsoft Visual Studio 2010 中的 SharePoint 开发工具创建一个新的 SharePoint 空项目。在创建该项目时,向该项目中添加一个将包含功能区自定义项的新的“空元素”项目项。

步骤 1.1:向 Element 中添加核心功能区标记

在元素清单内添加以下标记。

XML
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomAction Id="Ribbon.Documents.ApplyDocumentPrefix" Location="CommandUI.Ribbon">
    <CommandUIExtension>
      <CommandUIDefinitions>
        <CommandUIDefinition Location="Ribbon.Documents.Groups._children">
          <Group Id="Ribbon.Documents.ApplyDocumentPrefix"
                 Title="Document Naming Tools"
                 Description="Document Naming Tools Description"
                 Sequence="25"
                 Template="Ribbon.Templates.MsdnTemplate">
          </Group>
        </CommandUIDefinition>
      </CommandUIDefinitions>
      <CommandUIHandlers />
    </CommandUIExtension>
  </CustomAction>
</Elements>

这将向“文档”选项卡的一个名为“Document Naming Tools”的组集合中添加一个新组,如 <CommandUIDefinition Location="Ribbon.Documents.Groups._children"> 所示。该组包含一个由 25 个项组成的序列。内置 SharePoint 2010 服务器功能区中的组的编号从数字 10 开始按 10 递增。这意味着,此处定义的自定义组将在第三个位置显示。

另外,该组将不会使用某个现有组模板,而将使用一个自定义组模板。

步骤 1.2:添加自定义组模板

添加以下标记以向功能区添加自定义组。

XML
<CommandUIExtension>
  <CommandUIDefinitions>
    <CommandUIDefinition Location="Ribbon.Documents.Groups._children">
      <Group Id="Ribbon.Documents.ApplyDocumentPrefix" ... > 
        ...
      </Group>
    </CommandUIDefinition>
    <CommandUIDefinition Location="Ribbon.Templates._children">
      <GroupTemplate Id="Ribbon.Templates.MsdnTemplate">
        <Layout Title="MsdnHorizontal">
          <Section Type="OneRow">
            <Row>
              <ControlRef DisplayMode="Large" TemplateAlias="o1" />
              <ControlRef DisplayMode="Large" TemplateAlias="o2" />
              <ControlRef DisplayMode="Large" TemplateAlias="o3" />
            </Row>
          </Section>
        </Layout>
        <Layout Title="MsdnVertical">
          <Section Type="ThreeRow">
            <Row><ControlRef DisplayMode="Medium" TemplateAlias="o1" /></Row>
            <Row><ControlRef DisplayMode="Medium" TemplateAlias="o2" /></Row>
            <Row><ControlRef DisplayMode="Medium" TemplateAlias="o3" /></Row>
          </Section>
        </Layout>
       <Layout Title="MsdnVerticalTextOnly">
          <Section Type="ThreeRow">
            <Row><ControlRef DisplayMode="Menu" TemplateAlias="o1" /></Row>
            <Row><ControlRef DisplayMode="Menu" TemplateAlias="o2" /></Row>
            <Row><ControlRef DisplayMode="Menu" TemplateAlias="o3" /></Row>
          </Section>
        </Layout>
      </GroupTemplate>
    </CommandUIDefinition>
  <CommandUIDefinitions>
  <CommandUIHandlers />
</CommandUIExtension>

此模板(即“Ribbon.Templates.MsdnTemplate”)包含三个不同的布局。每个 <Layout /> 具有一个 Title 属性,<MaxSize /> 元素或 <Scale /> 元素中引用了该属性。这会告知功能区要用于布局的模板。每个 <Layout /> 元素均具有一个 <Section />,它使用 Type 属性定义组的呈现方式。这可以是 4 个用于呈现组的选项之一,这些选项包括使用一行、使用两行、使用三行或使用分隔线。有关详细信息,请参阅 Section 元素

<Section /> 包含一个或多个 <Row /> 元素,这些元素定义布局中的每个行。<Row /> 元素包含充当控件的占位符的 <ControlRef /> 元素。DisplayMode 属性告知功能区如何使用文本标签、大图标、小图标或 ControlRef 元素的架构中定义的其他选项之一来呈现控件。TemplateAlias 属性用于触发特定控件放置。在向组添加控件时,每个控件的 TemplateAlias 属性将与这些 TemplateAlias 属性之一匹配以准确指定控件的放置位置。

现在添加以下标记,以告知功能区如何通过使用已定义模板中的布局之一来呈现组。

XML
<CommandUIExtension>
  <CommandUIDefinitions>
    <CommandUIDefinition Location="Ribbon.Documents.Groups._children">
      <Group Id="Ribbon.Documents.ApplyDocumentPrefix" ... >
        ...
      </Group>
    </CommandUIDefinition>
    <CommandUIDefinition Location="Ribbon.Documents.Scaling._children">
      <MaxSize Id="Ribbon.Documents.Scaling.ApplyDocumentPrefix.MaxSize"
               GroupId="Ribbon.Documents.ApplyDocumentPrefix"
               Size="MsdnVertical"
               Sequence="15" />
    </CommandUIDefinition>
    <CommandUIDefinition Location="Ribbon.Templates._children">
      <GroupTemplate Id="Ribbon.Templates.MsdnTemplate">
        ...
      </GroupTemplate>
    </CommandUIDefinition>
  </CommandUIDefinitions>
  <CommandUIHandlers />
</CommandUIExtension>

图 12、13 和 14 演示如何仅通过将 <MaxSize Size="" /> 属性更改为不同的布局选项来更改组的呈现。



图 12. 使用 MsdnHorizontal 布局

使用 MsdnHorizontal 布局

图 13. 使用 MsdnVertical 布局

使用 MsdnVertical 布局

图 14. 使用 MsdnVerticalTextOnly 布局

使用 MsdnVerticalTextOnly 布局
步骤 1.3:向功能区组添加控件

接下来,向元素清单文件添加以下标记,以向组中添加几个按钮。

XML
<CommandUIExtension>
  <CommandUIDefinitions>
    <CommandUIDefinition Location="Ribbon.Documents.Groups._children">
      <Group Id="Ribbon.Documents.ApplyDocumentPrefix" ... > 
        <Controls Id="Ribbon.Documents.ApplyDocumentPrefix.Controls">
          <Button Id="Ribbon.Documents.ApplyDocumentPrefix.CustomHelpButton"
                  LabelText="Apply Document Prefix Help"
                  TemplateAlias="o1"
                  Sequence="15"
                  Image16by16="/_layouts/Images/ApplyDocumentPrefixRibbon/16x16Placeholder.png"
                  Image32by32="/_layouts/Images/ApplyDocumentPrefixRibbon/32x32Placeholder.png"
                  Command="ApplyDocumentPrefix.OnGetHelpApplyDocPrefix" />
          <Button Id="Ribbon.Documents.ApplyDocumentPrefix.CustomApplyPrefixButton"
                  LabelText="Apply Prefix w/ CommandUI Handler"
                  TemplateAlias="o2"
                  Sequence="17"
                  Image16by16="/_layouts/Images/ApplyDocumentPrefixRibbon/16x16Placeholder.png"
                  Image32by32="/_layouts/Images/ApplyDocumentPrefixRibbon/32x32Placeholder.png"
                  Command="ApplyDocumentPrefix.OnApplyDocPrefixUIHandler" />
          <Button Id="Ribbon.Documents.ApplyDocumentPrefix.CustomPageComponentButton"
                  LabelText="Apply Prefix w/ Page Component"
                  TemplateAlias="o3"
                  Sequence="19"
                  Image16by16="/_layouts/Images/ApplyDocumentPrefixRibbon/16x16Placeholder.png"
                  Image32by32="/_layouts/Images/ApplyDocumentPrefixRibbon/32x32Placeholder.png"
                  Command="ApplyDocumentPrefix.OnApplyDocPrefixPageComponent" />
        </Controls>
      </Group>
    </CommandUIDefinition>
  </CommandUIDefinitions>
  <CommandUIHandlers />
</CommandUIExtension>

请注意,每个按钮都会连接到其自己的命令。在完成功能区自定义项的可见部分后,紧接着是创建用于处理按钮的可用时间以及按钮被单击时执行的操作的命令。通过使用命令 UI 处理程序或页面组件可完成此操作。后面的两个步骤将演示如何实现这两点。

步骤 2:创建服务器功能区命令 UI 处理程序

在包含功能区可见自定义项的同一元素清单文件中定义命令 UI 处理程序。添加以下标记以创建首个命令 UI 处理程序。

XML
<CommandUIHandlers>
  <CommandUIHandler Command="ApplyDocumentPrefix.OnGetHelpApplyDocPrefix"
      CommandAction="javascript:
        var dialogOptions = {
          url: '/_layouts/ApplyDocumentPrefixRibbon/DocPrefixHelp.aspx',
          title: 'Apply Document Prefix Help',
          allowMaximize: true,
          showClose: true,
          width:500,
          height:400
        };
        SP.UI.ModalDialog.showModalDialog(dialogOptions); " />
</CommandUIHandlers>

通过使用 Command 属性将此命令连接到第一个按钮。CommandAction 属性会执行一些 JavaScript,这将使用指向一个稍后将创建的自定义应用程序页的 SharePoint 2010 对话框框架来打开一个对话框。

添加将与第二个按钮关联的另一个命令 UI 处理程序。此处理程序执行了大量工作,如以下标记所示。

XML
<CommandUIHandlers>
  <CommandUIHandler Command="ApplyDocumentPrefix.OnGetHelpApplyDocPrefix" ... />
  <CommandUIHandler Command="ApplyDocumentPrefix.OnApplyDocPrefixUIHandler"
      CommandAction="javascript:
        function dialogCallback(dialogResult, returnValue){
          SP.UI.Notify.addNotification(returnValue);
          SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
        }
      
        var selectedItems = SP.ListOperation.Selection.getSelectedItems();
        var selectedItemIds = '';
        var selectedItemIndex;
        for (selectedItemIndex in selectedItems){
          selectedItemIds += '|' + selectedItems[selectedItemIndex].id;
        }
       
        var dialogOptions = {
          url: '/_layouts/ApplyDocumentPrefixRibbon/DocPrefixPrompt.aspx?selectedItems='
          +selectedItemIds +'&amp;ListId=' +SP.ListOperation.Selection.getSelectedList(),
          title: 'Set Document Prefix',
          allowMaximize: false,
          showClose: false,
          width:500,
          height:400,
          dialogReturnValueCallback: dialogCallback
        };
        
        SP.UI.ModalDialog.showModalDialog(dialogOptions);"
      EnabledScript="javascript:
        function checkIsEnabled(){
          // Check items selected.
          var selectedItems = SP.ListOperation.Selection.getSelectedItems();
          var count = CountDictionary(selectedItems);
          return (count > 0);
        }; 
                      
        checkIsEnabled();"
    />
</CommandUIHandlers>

此处理程序更为复杂一些。首先,它创建一个新的 JavaScript 函数,此函数在关闭对话框时将用作回调。它在浏览器中显示一个通知(从页面右侧滑入的用黄色显示的消息),然后刷新页面上的列表视图 Web 部件。

然后,它创建一个在当前页面上选定的文档的分隔列表。最后,它创建一个新对话框,该对话框将打开一个新的应用程序页并传入选定项。当对话框关闭时,它将调用前面定义的 dialogCallback 方法。

它的另一个重要部分是 EnabledScript 属性。如果命令可用,则此属性返回的布尔值为 TRUE;如果命令不可用,则返回 FALSE。为了确定这一点,它将检查是否已在页面上选定其他选项。如果未选定,则将禁用命令和此按钮。

步骤 3:创建服务器功能区页面组件

在功能区中具有一个使用在命令 UI 处理程序中定义的命令的按钮之后,现在可以创建页面组件来处理其他命令。通过将新的 JavaScript 文件添加到 Visual Studio 项目(最好是添加到已映射到 {SharePoint Root}/TEMPLATE/LAYOUT/{ProjectName} 目录的文件夹中)来实现这一点。这将确保供所有网站使用的前端 Web 服务器上只有一个库实例。

步骤 3.1:声明页面组件对象

在将文件添加到项目后,将以下脚本添加到库以声明此库将包含的 JavaScript 对象,以及少量构造函数和初始化方法,它们将创建一个页面组件实例并将该实例添加到客户端功能区管理器。以下标记对此进行了演示。

C#
// Register the page component object.
Type.registerNamespace('ApplyDocumentPrefix');

// Helper methods to set up and initialize the page component.
ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent = function ApplyDocumentPrefix_PageComponent() {
  ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.initializeBase(this);
}

ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.initialize = function () {
  ExecuteOrDelayUntilScriptLoaded(Function.createDelegate(null, 
  ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.initializePageComponent), 'SP.Ribbon.js');
}

ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.initializePageComponent = function () {
  var ribbonPageManager = SP.Ribbon.PageManager.get_instance();
  if (ribbonPageManager !== null) {
    ribbonPageManager.addPageComponent(ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.instance);
  }
}

步骤 3.2:创建页面组件对象原型和 init 方法

现在,为对象原型和核心 init 方法添加以下脚本。从其名称可以了解,此方法将通过创建几个数组来初始化对象,这些数组会列出命令并将命令映射到对象内的特定函数。

C#
// Page component object.
ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.prototype = {

  init: function ApplyDocumentPrefix_PageComponento$init() {
    // Array of commands that can be handled that are associated with handler methods.
    this.handledCommands = new Object;
    this.handledCommands['ApplyDocumentPrefix.OnApplyDocPrefixPageComponent'] = 
    this.onApplyDocPrefixPageComponent;

    // Array of commands that can be handled that are associated with canHandler methods.
    this.canHandledCommands = new Object;
    this.canHandledCommands['ApplyDocumentPrefix.OnApplyDocPrefixPageComponent'] = 
    this.onApplyDocPrefixPageComponent_canExecute;

    // Array of commands.
    this.commandList = ['ApplyDocumentPrefix.OnApplyDocPrefixPageComponent'];
  },
  getId: function ApplyDocumentPrefixPageComponent_PageComponent$getId() {
    return "ApplyDocumentPrefixPageComponent";
  },  ...
}

请注意,在定义功能区自定义项的可见组件时,数组中命令的名称与前面添加的 <Button Command="" /> 属性的值匹配。

步骤 3.3:添加方法以告知功能区管理器此页面组件处理的命令以及处理命令的方式和时间

接下来,添加四个方法,这四个方法告知客户端功能区管理器哪些命令可用以及处理这些命令的方式和时间,如以下代码所示。

C#
ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.prototype = {
  ...
  getFocusedCommands: function ApplyDocumentPrefixPageComponent_PageComponent$getFocusedCommands() {
    return [];
  },

  getGlobalCommands: function ApplyDocumentPrefixPageComponent_PageComponent$getGlobalCommands() {
    return this.commandList;
  },

  canHandleCommand: function 
  ApplyDocumentPrefixPageComponent_PageComponent$canHandleCommand(commandID) {
    var canHandle = this.handledCommands[commandID];
    if (canHandle)
      return this.canHandledCommands[commandID]();
    else
      return false;
  },

  handleCommand: function 
  ApplyDocumentPrefixPageComponent_PageComponent$handleCommand(commandID, 
  properties, sequence) {
    return this.handledCommands[commandID](commandID, properties, sequence);
  },
  ...
}

canHandleCommand 方法与命令 UI 处理程序的 EnabledScript 属性的功能相同,这些属性告知功能区管理器命令是否可用。在上一个代码段中,该方法使用在对象的初始值设定项中创建的数组来调用对象中的特定方法。handleCommand 的功能与命令 UI 处理程序的 CommandAction 属性的功能相似。它也使用一个数组以指向指定命令的另一个方法。

步骤 3.4:添加特定命令方法

接下来是,添加已在对象的初始值设定项中定义且被调用的方法,以确定命令何时可用 (onApplyDocPrefixPageComponent_canExecute) 以及执行命令时发生的操作 (onApplyDocPrefixPageComponent)。

C#
ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.prototype = {
  ...
  onApplyDocPrefixPageComponent: function () {
    var selectedItems = SP.ListOperation.Selection.getSelectedItems();
    var selectedItemIds = '';
    var selectedItemIndex;
    for (selectedItemIndex in selectedItems) {
      selectedItemIds += '|' + selectedItems[selectedItemIndex].id;
    }

    var dialogOptions = {
      url: '/_layouts/ApplyDocumentPrefixRibbon/DocPrefixPrompt.aspx?selectedItems=' + 
      selectedItemIds + '&ListId=' + SP.ListOperation.Selection.getSelectedList(),
      title: 'Set Document Prefix',
      allowMaximize: false,
      showClose: false,
      width: 500,
      height: 400,
      dialogReturnValueCallback: PageComponentCallback
    };

    SP.UI.ModalDialog.showModalDialog(dialogOptions);
  },

  onApplyDocPrefixPageComponent_canExecute: function () {
    var selectedItems = SP.ListOperation.Selection.getSelectedItems();
    var count = CountDictionary(selectedItems);
    return (count > 0);
  }
  ...
}
function PageComponentCallback(dialogResult, returnValue) {
  SP.UI.Notify.addNotification(returnValue);
  SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK);
}

步骤 3.5:添加最后对象注册、初始化和通知脚本

完成页面组件对象之后,最后的步骤是:在页面上注册该对象;创建该对象;通知客户端功能区,正在等待此脚本完成该对象加载的任何操作已完成加载和初始化。这些步骤如以下代码所示。

C#
ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.prototype = {
  ...
}
ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.registerClass
('ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent', 
CUI.Page.PageComponent);
ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.instance = 
new ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent();
ApplyDocumentPrefix.ApplyDocumentPrefixPageComponent.initialize();
SP.SOD.notifyScriptLoadedAndExecuteWaitingJobs("ApplyDocumentPrefix.UI.js");

此时,已创建页面组件,并且现在可将该组件添加到页面。

步骤 4:创建服务器功能区页面组件加载程序

接下来,必须将页面组件添加到页面。在此示例中,自定义服务器控件用于执行此操作。向项目中添加一个新的 Empty Element SharePoint 项目项。使用 <Control /> 元素以注册一个具有以下标记的新委托控件。

XML
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Control Id="AdditionalPageHead"
           ControlClass="MSDN.SharePoint.Samples.ApplyDocumentPrefix.PageComponentScriptLoader"
           ControlAssembly="$SharePoint.Project.AssemblyFullName$" />
</Elements>

这会将服务器控件 PageComponentScriptLoader 添加到当前网站中所有页面的 AdditionalPageHead 内容占位符中(不管功能的范围如何)。现在,向 SharePoint 项目项中添加一个新的代码文件,并添加以下代码以实现服务器控件。

C#
public class PageComponentScriptLoader : WebControl {
  protected override void OnPreRender(EventArgs e) {
    SPRibbon ribbon = SPRibbon.GetCurrent(this.Page);

    // Ensure ribbon exists and current list is a document library 
    // (otherwise, no need for extra ecmascript load).
    if (ribbon != null && SPContext.Current.List is SPDocumentLibrary) {
      // Load dependencies if not already on the page.
      ScriptLink.RegisterScriptAfterUI(this.Page, "SP.Ribbon.js", false, true);

      // Load page component.
.      ScriptLink.RegisterScriptAfterUI(this.Page, "ApplyDocumentPrefixRibbon/ApplyDocumentPrefix.UI.js", false, true);
    }

    base.OnPreRender(e);
  }
}

这将首先获取对服务器功能区的引用。当用户使用文档库时,它将首先验证页面上的从属脚本,然后再注册页面组件。

此处的最后一个步骤是,将 <SafeControl /> 项添加到网站的 web.config 文件。虽然“空元素”SharePoint 项目项不自动添加 <SafeControl /> 项,但它为我们提供了执行此操作的方法。在 Visual Studio 2010 中的“解决方案资源管理器”中,选择“空元素”。在“属性”窗口中,单击“[…]”生成器按钮,如图 15 所示。



图 15. 使用 Visual Studio 2010 中的 SharePoint 开发工具手动添加安全控制项

手动添加安全控制项

在“安全控制项”对话框中,添加一个新项,然后验证命名空间是否与先前创建的服务器控件的命名空间匹配。



图 16.“安全控制项”对话框

“安全控制项”对话框
步骤 5:创建自定义对话框

最后一个步骤是,创建由自定义功能区命令调用的且执行大部分工作的对话框。将一个新的“应用程序页”SharePoint 项目项添加到 Visual Studio 项目。

步骤 5.1:实现对话框的可见部分 (.aspx)

在此示例中,对话框使用了几个 SharePoint 管理表单用户控件,因此需要一些引用,如以下代码所示。

<%@ Register TagPrefix="wssuc" TagName="InputFormSection" 
Src="~/_controltemplates/InputFormSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormControl" 
src="~/_controltemplates/InputFormControl.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" 
Src="~/_controltemplates/ButtonSection.ascx" %>

PlaceHolderMain 内容占位符中,添加以下标记以创建带 ASP.NET DataList 控件的表单,该控件将显示所有选定文档的列表。

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
  <table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tr>
      <td>
        <wssuc:InputFormSection runat="server" 
                                Title="Selected Documents" 
                                Description="The following documents have been selected 
                                to have the specified prefix added to their titles.">
          <Template_InputFormControls>
                        <tr>
                          <td>
                            <asp:DataList ID="SelectedDocumentsDataList" runat="server"
                            RepeatColumns="2" CellPadding="2" CellSpacing="5">
                  <ItemTemplate><li><%# DataBinder.Eval(Container.DataItem, 
                  "File.Name").ToString()%></li></ItemTemplate>
                </asp:DataList>
                          </td>
                        </tr>
                  </Template_InputFormControls>
        </wssuc:InputFormSection>

        <wssuc:InputFormSection runat="server" 
                                Title="Document Prefix" 
                                Description="Prefix to add to the selected document 
                                titles.">
          <Template_InputFormControls>
            <wssuc:InputFormControl LabelText="Prefix to add to the selected documents:"
            runat="server">
              <Template_control>
                <asp:TextBox ID="DocumentPrefixTextBox" runat="server" />
              </Template_control>
            </wssuc:InputFormControl>
                  </Template_InputFormControls>
        </wssuc:InputFormSection>

        <wssuc:ButtonSection runat="server" ShowStandardCancelButton="FALSE" 
        TopButtons="TRUE">
          <Template_Buttons>
            <asp:Button ID="SetPrefixButton" class="ms-ButtonHeightWidth" runat="server" 
            Text="Set Prefix" OnClick="OnClickSetPrefixButton" />
            <asp:Button ID="CancelButton" class="ms-ButtonHeightWidth" runat="server" 
            Text="Cancel" OnClientClick=
            "SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.cancel,
            'Assignment of prefix cancelled.'); return false;" />
          </Template_Buttons>
        </wssuc:ButtonSection>
      </td>
    </tr>
  </table>
</asp:Content>

步骤 5.2:实现代码隐藏中的业务逻辑 (.aspx.cs)

此应用程序页的代码隐藏非常简单。它需要执行以下任务:

  • 获取在 QueryString 上传入的所有选定文档的列表。

  • 获取对包含在 QueryString 上传入的文档的列表的引用。

  • 显示通过将选定文档的集合绑定到 ASP.NET DataList 控件来选定的项。

  • 单击时,将更新所有选定项的文件名。

  • 完成后,将一些 JavaScript 写入将关闭对话框的 PlaceHolderAdditionalPageHead 内容占位符,这会将结果发送回调用页面。

向应用程序页的代码隐藏文件中添加以下代码。

C#
public partial class DocPrefixPrompt : LayoutsPageBase {
  List<SPListItem> _selectedListItems = new List<SPListItem>();

  protected override void OnLoad(EventArgs e) {
    // Get all the selected documents.
    SPList selectedDocumentLibrary = 
        SPContext.Current.Web.Lists[new Guid(
                  Request.QueryString["ListId"]
        )];
    string[] selectedItems = 
      Request.QueryString["selectedItems"].ToString().Split('|');

    for (int index = 1; index < selectedItems.Length; index++) {
      _selectedListItems.Add(
        selectedDocumentLibrary.GetItemById(
          int.Parse(selectedItems[index]))
      );
    }

    // Bind to the repeater.
    SelectedDocumentsDataList.DataSource = _selectedListItems;
    SelectedDocumentsDataList.DataBind();
  }

  protected void OnClickSetPrefixButton(object sender, EventArgs e) {
    foreach (SPListItem listItem in _selectedListItems) {
      listItem["Name"] = 
        DocumentPrefixTextBox.Text + " " + listItem.File.Name;
      listItem.Update();
    }

    CloseDialogOnSuccess();
  }
  private void CloseDialogOnSuccess() {
    ContentPlaceHolder pageHead = this.Master.FindControl("PlaceHolderAdditionalPageHead") as ContentPlaceHolder;
    if (pageHead != null)
      pageHead.Controls.Add(
        new LiteralControl("
<script language='javascript'>
ExecuteOrDelayUntilScriptLoaded(closeDialog,'sp.js');
function closeDialog(){
  SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK, 'Prefix assigned to selected documents.');
}
</script>"));
  }
}

步骤 6:部署和测试自定义 Web 部件

在保存所有更改后,通过在 Visual Studio 2010 中按 F5,或通过在“调试”菜单上单击“开始调试”来部署自定义 Web 部件。

在打开工作组网站的初始网页时,单击“共享文档库”。通过选择功能区上的“文档”选项卡可使功能区自定义项可见,如图 9 所示。在选定某个文档后,将启用自定义组中的所有按钮,如图 10 所示。单击两个“Apply Prefix […]”按钮之一将触发对话框,如图 11 所示。

当用户单击“Set Prefix”按钮时,该对话框将更新选定的文件名,然后关闭。之后,命令将发出一条表明已应用更改的通知消息,并刷新列表视图 Web 部件。

自定义 SharePoint 2010 Server 功能区开发提示和技巧

在自定义 SharePoint 2010 服务器功能区时,开发人员可以进行各种更改。提供了各种可用控件、可重用的模板以及可进行修改或添加操作的组和选项卡。幸运的是,大多数功能区是以声明方式构造的,或(对于页面组件)驻留在比已编译代码更易读取的脚本文件中。这意味着,在安装 SharePoint 后,开发人员将拥有大量可用的代码示例。

技巧在于了解这些示例的查找位置和查找方式。可在全局网站定义的 {SharePoint Root}\TEMPLATE\GLOBAL\XML\CMDUI.XML 文件中找到基服务器功能区和所有功能区声明。通常,可以在各种功能中找到由 SharePoint Server 2010 企业内容管理等项进行的其他更改。只需在所有 *.xml 文件中搜索 <CommandUIExtension> 元素,就将显示包含功能区扩展的所有功能的列表。

以下各节针对自定义功能区的一些常见情况,帮助开发人员找到正确的方向。

查找选项卡、组以及控件名称和序列

创建新选项卡、将组添加到选项卡或修改现有控件是一项常见任务。第一个步骤是查找选项卡的 ID 或名称。SharePoint 2010 SDK 包含所有选项卡及其位置的列表。有关详细信息,请参阅默认服务器功能区自定义位置。由于选项卡名称的描述很清晰,因此很容易了解哪个选项卡对应哪个名称。此页还包含所有组及其控件的名称的列表。仅缺少序列和模板别名。

若要查找序列和模板别名,请打开 CMDUI.xml 文件并搜索相关选项卡、组或 SDK 中列出的控件 ID。这样,您将获得可回答有关控件的任何问题的相关项。

始终获取清除页面请求:清除浏览器缓存

在自定义功能区时,开发人员肯定会编写 JavaScript 并引用图像。JavaScript 和图像及功能区自定义项的数量取决于应用程序的复杂度。

在重构 SharePoint 2010 UI 并开发功能区时,Microsoft 会侧重于尽可能地限制页面的权重或页面上所需的所有内容的组合大小。为此,SharePoint 会主动在浏览器中缓存 CSS 文件和 JavaScript 库。这反过来为用户提供了更短的页面加载时间,因为后续请求上请求的文件更少。

虽然这为用户带来了好处,但同时也为开发人员带来了挑战。由于浏览器未下载最新的脚本或自定义项,因此无法显示在正在调试的会话间所做的更改。在部署解决方案后等待加载解决方案时的最佳做法是,使用 Internet Explorer 开发人员工具栏



图 17. 清除 IE 缓存并强制下载所有文件

清除 IE 缓存并强制下载文件

Internet Explorer 开发人员工具栏还提供了浏览脚本文件的方法。若要验证浏览器是否下载了最新版本的自定义脚本文件,请使用“脚本”选项卡,并选择已由页面加载的脚本文件。如果未列出外部脚本文件,则不会将其添加到页面。图 18 演示了从上一个示例中选择页面组件外部脚本库并查看其内容。



图 18. 使用 IE 开发人员工具栏进行的脚本调试

使用“IE 开发工具”工具栏的脚本调试

结论

本文说明了构成 Microsoft SharePoint 2010 服务器功能区的核心组件。然后,向您演示如何创建使用不同的自定义技术的两个示例功能区更改。另外,本文还提供有关何时考虑不同的命令实现选项(命令 UI 处理程序或页面组件)的指南,并提供了有关功能区自定义项开发的提示和技巧。

其他资源

有关详细信息,请参阅以下资源:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值