演练:在服务器资源管理器扩展中调入 SharePoint 客户端对象模型
本演练演示如何从**“服务器资源管理器”中的“SharePoint 连接”**节点扩展调用 SharePoint 客户端对象模型。 有关使用 SharePoint 客户端对象模型的更多信息,请参见调入 SharePoint 对象模型。
本演练将演示以下任务:
创建一个 Visual Studio 扩展,此扩展通过以下方式扩展**“服务器资源管理器”的“SharePoint 连接”**节点:
此扩展会在**“服务器资源管理器”中的每个 SharePoint 网站节点下添加一个新的“Web 部件库”**节点。 该新节点包含的每个子节点均表示网站上的 Web 部件库中的一个 Web 部件。
此扩展会定义一个表示 Web 部件实例的新节点类型。 该新节点类型是新**“Web 部件库”节点下的子节点的基础。 该新 Web 部件节点类型在“属性”**窗口中显示有关其表示的 Web 部件的信息。
生成 Visual Studio 扩展 (VSIX) 包以部署扩展。
调试并测试扩展。
提示
您在本演练中创建的扩展与您在演练:扩展服务器资源管理器以显示 Web 部件中创建的扩展类似。 但是,该演练使用的是 SharePoint 服务器对象模型,而本演练通过使用客户端对象模型来完成相同的任务。
系统必备
您需要在开发计算机上安装以下组件才能完成本演练:
支持的 Microsoft Windows、SharePoint 和 Visual Studio 版本。 有关更多信息,请参见开发 SharePoint 解决方案的要求。
Visual Studio 2010 SDK。 本演练使用此 SDK 中的**“VSIX 项目”**模板来创建 VSIX 包以部署扩展。 有关更多信息,请参见扩展 Visual Studio 中的 SharePoint 工具。
了解以下概念很有用,但对于完成本演练并不是必需的:
使用 SharePoint 客户端对象模型。 有关更多信息,请参见 Managed Client Object Model(托管客户端对象模型)。
Microsoft SharePoint Services 中的 Web 部件。 有关更多信息,请参见 Web Parts Overview(Web 部件概述)。
创建项目
若要完成本演练,您需要创建以下两个项目:
一个用于创建 VSIX 包以部署**“服务器资源管理器”**扩展的 VSIX 项目。
一个用于实现**“服务器资源管理器”**扩展的类库项目。
从创建项目开始本演练。
创建 VSIX 项目
启动 Visual Studio。
在**“文件”菜单上指向“新建”,再单击“项目”**。
在**“新建项目”对话框中,展开“Visual C#”或“Visual Basic”节点,然后单击“扩展性”**。
提示
只有在安装 Visual Studio 2010 SDK 之后,“扩展性”节点才可用。 有关更多信息,请参见前面的系统必备部分。
在对话框顶部的组合框中,选择**“.NET Framework 4”**。 SharePoint 工具扩展需要此版本的 .NET Framework 中的功能。
单击**“VSIX 项目”**模板。
在**“名称”**框中键入 WebPartNode。
单击**“确定”**。
Visual Studio 将**“WebPartNode”项目添加到“解决方案资源管理器”**中。
创建扩展项目
在**“解决方案资源管理器”中,右击解决方案节点,单击“添加”,再单击“新建项目”**。
提示
在 Visual Basic 项目中,仅当在“选项”对话框 ->“项目和解决方案”->“常规”中选中“总是显示解决方案”复选框时,解决方案节点才会出现在“解决方案资源管理器”中。
在**“新建项目”对话框中,展开“Visual C#”或“Visual Basic”节点,然后单击“Windows”**。
在对话框顶部的组合框中,选择**“.NET Framework 4”**。
选择**“类库”**项目模板。
在**“名称”**框中键入 WebPartNodeExtension。
单击**“确定”**。
Visual Studio 将**“WebPartNodeExtension”**项目添加到解决方案中,并打开默认的 Class1 代码文件。
从项目中删除 Class1 代码文件。
配置扩展项目
在编写代码以创建扩展之前,您必须向项目中添加代码文件和程序集引用并更新默认命名空间。
配置项目
在**“WebPartNodeExtension”**项目中,添加具有下列名称的两段代码:
SiteNodeExtension
WebPartNodeTypeProvider
在**“项目”菜单上,单击“添加引用”**。
在**“.NET”选项卡上,按住 Ctrl 的同时选择下列程序集,然后单击“确定”**:
Microsoft.SharePoint.Client
Microsoft.SharePoint.Client.Runtime
Microsoft.VisualStudio.SharePoint
System.ComponentModel.Composition
System.Windows.Forms
在**“项目”菜单上选择“WebPartNodeExtension 属性”**。
随即打开**“项目设计器”**。
单击**“应用程序”**选项卡。
在**“默认命名空间”框 (C#) 或“根命名空间”**框 (Visual Basic) 中,键入 ServerExplorer.SharePointConnections.WebPartNode。
为新节点创建图标
为**“服务器资源管理器”扩展创建两个图标:一个图标用于新的“Web 部件库”节点,另一个图标用于“Web 部件库”**节点下的每个子 Web 部件节点。 在本演练后面的部分中,您将编写代码以将这些图标与节点相关联。
为节点创建图标
在 WebPartNodeExtension 项目的**“项目设计器”中,单击“资源”**选项卡。
单击**“此项目不包含默认资源文件,单击此处创建一个资源文件。”**。
Visual Studio 会创建一个资源文件并在设计器中打开此文件。
在设计器的顶部,单击**“添加”按钮上的下拉箭头,再单击“添加新图标”**。
为新图标名称键入 WebPartsNode,然后单击**“添加”**。
新图标将在**“图像编辑器”**中打开。
编辑 16x16 版本的图标,使得设计出的图标可以让您轻松地识别。
单击 32x32 版本的图标。
在**“图像”菜单上单击“删除图像类型”**。
重复步骤 3 到步骤 7,将第二个图标添加到项目资源中。 将此图标命名为 WebPart。
在**“解决方案资源管理器”中的“WebPartNodeExtension”项目的“Resources”文件夹下,选择“WebPartsNode.ico”**。
在**“属性”窗口中,单击“生成操作”旁边的下拉箭头,然后选择“嵌入的资源”**。
对于**“WebPart.ico”**,重复最后两个步骤。
向服务器资源管理器中添加 Web 部件库节点
创建一个类,此类可将新的**“Web 部件库”节点添加到每个 SharePoint 网站节点中。 若要添加新节点,此类应实现 IExplorerNodeTypeExtension 接口。 每当需要扩展“服务器资源管理器”**中的现有节点的行为(例如向某个节点添加新的子节点)时,就要实现此接口。
向服务器资源管理器中添加 Web 部件库节点
在 WebPartNodeExtension 项目中,双击 SiteNodeExtension 代码文件。
将下面的代码粘贴到此文件中。
提示
添加此代码后,项目将会出现一些编译错误。 在添加后面的步骤中的代码之后,这些错误将消失。
Imports System.Collections.Generic Imports System.ComponentModel.Composition Imports Microsoft.SharePoint.Client Imports Microsoft.VisualStudio.SharePoint Imports Microsoft.VisualStudio.SharePoint.Explorer Namespace ServerExplorer.SharePointConnections.WebPartNode ' Export attribute: Enables Visual Studio to discover and load this extension. ' ExplorerNodeType attribute: Indicates that this class extends SharePoint site nodes in Server Explorer. ' SiteNodeExtension class: Represents an extension of SharePoint site nodes in Server Explorer. <Export(GetType(IExplorerNodeTypeExtension))> _ <ExplorerNodeType(ExplorerNodeTypes.SiteNode)> _ Friend Class SiteNodeExtension Implements IExplorerNodeTypeExtension Private siteUrl As System.Uri = Nothing Private Sub Initialize(ByVal nodeType As IExplorerNodeType) _ Implements IExplorerNodeTypeExtension.Initialize ' The NodeChildrenRequested event is raised when the user expands the ' SharePoint site node in Server Explorer. AddHandler nodeType.NodeChildrenRequested, AddressOf NodeChildrenRequested End Sub ' Creates the new Web Part Gallery node with the specified icon. Private Sub NodeChildrenRequested(ByVal Sender As Object, ByVal e As ExplorerNodeEventArgs) ' Get the site URL so that it can be used later to access the site ' by using the SharePoint client object model. siteUrl = e.Node.Context.SiteUrl ' The CreateWebPartNodes argument is a delegate that Visual Studio calls ' to create the child nodes under the Web Part Gallery node. e.Node.ChildNodes.AddFolder("Web Part Gallery", My.Resources.WebPartsNode.ToBitmap(), _ AddressOf CreateWebPartNodes) End Sub ' Creates individual Web Part nodes under the new Web Part Gallery node. Private Sub CreateWebPartNodes(ByVal parentNode As IExplorerNode) ' Use the SharePoint client object model to get items from the Web Part gallery. Dim Context As ClientContext = New ClientContext(siteUrl.AbsoluteUri) Dim WebPartsGallery As List = Context.Web.GetCatalog(CType(ListTemplateType.WebPartCatalog, Integer)) Dim WebParts As ListItemCollection = WebPartsGallery.GetItems(New CamlQuery()) ' Request the FieldValuesAsText property values with the Web Part items. Context.Load(WebParts, Function(listItems) listItems.Include(Function(i) i.FieldValuesAsText)) Context.ExecuteQuery() If WebParts IsNot Nothing Then For Each WebPart As ListItem In WebParts ' Create a new annotation object to store the current Web Part item with the new node. Dim Annotations = New Dictionary(Of Object, Object)() Annotations.Add(GetType(ListItem), WebPart) ' Create the new node for the current Web Part item. parentNode.ChildNodes.Add(WebPartNodeTypeProvider.WebPartNodeTypeId, _ WebPart.FieldValuesAsText.FieldValues("Title"), Annotations) Next End If End Sub End Class End Namespace
using System.Collections.Generic; using System.ComponentModel.Composition; using Microsoft.SharePoint.Client; using Microsoft.VisualStudio.SharePoint; using Microsoft.VisualStudio.SharePoint.Explorer; namespace ServerExplorer.SharePointConnections.WebPartNode { // Enables Visual Studio to discover and load this extension. [Export(typeof(IExplorerNodeTypeExtension))] // Indicates that this class extends SharePoint site nodes in Server Explorer. [ExplorerNodeType(ExplorerNodeTypes.SiteNode)] // Represents an extension of SharePoint site nodes in Server Explorer. internal class SiteNodeExtension : IExplorerNodeTypeExtension { private System.Uri siteUrl = null; public void Initialize(IExplorerNodeType nodeType) { // The NodeChildrenRequested event is raised when the user expands the // SharePoint site node in Server Explorer. nodeType.NodeChildrenRequested += NodeChildrenRequested; } // Creates the new Web Part Gallery node with the specified icon. private void NodeChildrenRequested(object sender, ExplorerNodeEventArgs e) { // Get the site URL so that it can be used later to access the site // by using the SharePoint client object model. siteUrl = e.Node.Context.SiteUrl; // The CreateWebPartNodes argument is a delegate that Visual Studio calls // to create the child nodes under the Web Part Gallery node. e.Node.ChildNodes.AddFolder("Web Part Gallery", Properties.Resources.WebPartsNode.ToBitmap(), CreateWebPartNodes); } // Creates individual Web Part nodes under the new Web Part Gallery node. private void CreateWebPartNodes(IExplorerNode parentNode) { // Use the SharePoint client object model to get items from the Web Part gallery. ClientContext context = new ClientContext(siteUrl.AbsoluteUri); List webPartsGallery = context.Web.GetCatalog((int)ListTemplateType.WebPartCatalog); ListItemCollection webParts = webPartsGallery.GetItems(new CamlQuery()); // Request the FieldValuesAsText property values with the Web Part items. context.Load(webParts, listItems => listItems.Include(i => i.FieldValuesAsText)); context.ExecuteQuery(); if (webParts != null) { foreach (ListItem webPart in webParts) { // Create a new annotation object to store the current Web Part item with the new node. var annotations = new Dictionary<object, object>() { { typeof(ListItem), webPart } }; // Create the new node for the current Web Part item. parentNode.ChildNodes.Add(WebPartNodeTypeProvider.WebPartNodeTypeId, webPart.FieldValuesAsText.FieldValues["Title"], annotations); } } } } }
定义表示 Web 部件的节点类型
创建一个类,此类定义表示 Web 部件的新节点类型。 Visual Studio 使用此新节点类型来显示**“Web 部件库”**节点下的子节点。 其中的每个子节点均表示 SharePoint 网站上的一个 Web 部件。
若要定义新节点类型,此类应实现 IExplorerNodeTypeProvider 接口。 每当需要在**“服务器资源管理器”**中定义新的节点类型时,就要实现此接口。
定义 Web 部件节点类型
在 WebPartNodeExtension 项目中,双击 WebPartNodeTypeProvider 代码文件。
将下面的代码粘贴到此文件中。
Imports System Imports System.Collections.Generic Imports System.Windows.Forms Imports System.ComponentModel.Composition Imports Microsoft.SharePoint.Client Imports Microsoft.VisualStudio.SharePoint Imports Microsoft.VisualStudio.SharePoint.Explorer Namespace ServerExplorer.SharePointConnections.WebPartNode ' Export attribute: Enables Visual Studio to discover and load this extension. ' ExplorerNodeType attribute: Specifies the ID for this new node type. ' WebPartNodeTypeProvider class: Defines a new node type that represents a Web Part on a SharePoint site. <Export(GetType(IExplorerNodeTypeProvider))> _ <ExplorerNodeType(WebPartNodeTypeProvider.WebPartNodeTypeId)> _ Friend Class WebPartNodeTypeProvider Implements IExplorerNodeTypeProvider Friend Const WebPartNodeTypeId As String = "Contoso.WebPart" Private Sub InitializeType(ByVal typeDefinition As IExplorerNodeTypeDefinition) _ Implements IExplorerNodeTypeProvider.InitializeType typeDefinition.DefaultIcon = My.Resources.WebPart.ToBitmap() typeDefinition.IsAlwaysLeaf = True AddHandler typeDefinition.NodePropertiesRequested, AddressOf NodePropertiesRequested AddHandler typeDefinition.NodeMenuItemsRequested, AddressOf NodeMenuItemsRequested End Sub ' Retrieves properties that are displayed in the Properties window when ' a Web Part node is selected. Private Sub NodePropertiesRequested(ByVal Sender As Object, _ ByVal e As ExplorerNodePropertiesRequestedEventArgs) Dim webPart = e.Node.Annotations.GetValue(Of ListItem)() Dim propertySource = e.Node.Context.CreatePropertySourceObject( _ webPart.FieldValuesAsText.FieldValues) e.PropertySources.Add(propertySource) End Sub Private Sub NodeMenuItemsRequested(ByVal Sender As Object, _ ByVal e As ExplorerNodeMenuItemsRequestedEventArgs) Dim WebPartNodeMenuItem As IMenuItem = e.MenuItems.Add("Display Message") AddHandler WebPartNodeMenuItem.Click, AddressOf MenuItemClick End Sub Private Sub MenuItemClick(ByVal Sender As Object, ByVal e As MenuItemEventArgs) Dim ParentNode As IExplorerNode = TryCast(e.Owner, IExplorerNode) If ParentNode IsNot Nothing Then Dim webPart = ParentNode.Annotations.GetValue(Of ListItem)() MessageBox.Show("You clicked the context menu for the following Web part: " & _ webPart.FieldValuesAsText.FieldValues("Title") + ".", "Web Part Menu Command") End If End Sub End Class End Namespace
using System; using System.Collections.Generic; using System.Windows.Forms; using System.ComponentModel.Composition; using Microsoft.SharePoint.Client; using Microsoft.VisualStudio.SharePoint; using Microsoft.VisualStudio.SharePoint.Explorer; namespace ServerExplorer.SharePointConnections.WebPartNode { // Enables Visual Studio to discover and load this extension. [Export(typeof(IExplorerNodeTypeProvider))] // Specifies the ID for this new node type. [ExplorerNodeType(WebPartNodeTypeProvider.WebPartNodeTypeId)] // Defines a new node type that represents a Web Part on a SharePoint site. internal class WebPartNodeTypeProvider : IExplorerNodeTypeProvider { internal const string WebPartNodeTypeId = "Contoso.WebPart"; public void InitializeType(IExplorerNodeTypeDefinition typeDefinition) { typeDefinition.DefaultIcon = Properties.Resources.WebPart.ToBitmap(); typeDefinition.IsAlwaysLeaf = true; typeDefinition.NodePropertiesRequested += NodePropertiesRequested; typeDefinition.NodeMenuItemsRequested += NodeMenuItemsRequested; } // Retrieves properties that are displayed in the Properties window when // a Web Part node is selected. private void NodePropertiesRequested(object sender, ExplorerNodePropertiesRequestedEventArgs e) { var webPart = e.Node.Annotations.GetValue<ListItem>(); object propertySource = e.Node.Context.CreatePropertySourceObject( webPart.FieldValuesAsText.FieldValues); e.PropertySources.Add(propertySource); } private void NodeMenuItemsRequested( object sender, ExplorerNodeMenuItemsRequestedEventArgs e) { e.MenuItems.Add("Display Message").Click += MenuItemClick; } private void MenuItemClick(object sender, MenuItemEventArgs e) { IExplorerNode parentNode = e.Owner as IExplorerNode; if (parentNode != null) { var webPart = parentNode.Annotations.GetValue<ListItem>(); MessageBox.Show("You clicked the context menu for the following Web part: " + webPart.FieldValuesAsText.FieldValues["Title"] + ".", "Web Part Menu Command"); } } } }
检查点
演练进行到此时,**“Web 部件库”**节点的所有代码都位于项目中。 生成解决方案以确保编译项目时不会出错。
生成解决方案
- 在**“生成”菜单上,选择“生成解决方案”**。
创建 VSIX 包以部署扩展
若要部署扩展,请使用解决方案中的 VSIX 项目来创建 VSIX 包。 首先,通过修改项目中包含的 source.extension.vsixmanifest 文件来配置 VSIX 包。 然后,通过生成解决方案来创建 VSIX 包。
配置 VSIX 包
在**“解决方案资源管理器”的“WebPartNode”项目下,双击 WebPartNode 项目中的“source.extension.vsixmanifest”**文件。
Visual Studio 将在清单编辑器中打开该文件。 source.extension.vsixmanifest 文件是所有 VSIX 包必需的 extension.vsixmanifest 文件的基础。 有关此文件的更多信息,请参见VSIX Extension Schema Reference。
在**“产品名称”**框中,键入“服务器资源管理器的 Web 部件库节点”。
在**“作者”**框中键入 Contoso。
在**“说明”**框中,键入“向服务器资源管理器中的 SharePoint 连接节点添加自定义 Web 部件库节点”。
在编辑器的**“内容”部分中,单击“添加内容”**按钮。
在**“添加内容”对话框的“选择内容类型”列表框中,选择“MEF 组件”**。
提示
此值对应于 extension.vsixmanifest 文件中的 MefComponent 元素。 此元素指定 VSIX 包中的扩展程序集的名称。 有关更多信息,请参见 MEFComponent Element (VSX Schema)。
在**“选择源”下,单击“项目”单选按钮,并在其旁边的列表框中选择“WebPartNodeExtension”**。
单击**“确定”**。
在**“生成”菜单上,单击“生成解决方案”**。 确保编译解决方案时不会出错。
打开 WebPartNode 项目的生成输出文件夹。 确保此文件夹此时包含 WebPartNode.vsix 文件。
默认情况下,生成输出文件夹为 包含项目文件的文件夹下的 ..\bin\Debug 文件夹。
测试扩展
此时,您可以对**“服务器资源管理器”中的新“Web 部件库”节点进行测试。 首先,在 Visual Studio 的实验实例中开始调试扩展项目。 然后,在 Visual Studio 的实验实例中使用新的“Web 部件”**节点。
开始调试扩展
利用管理员特权重新启动 Visual Studio,并打开**“WebPartNode”**解决方案。
在 WebPartNodeExtension 项目中,打开 SiteNodeExtension 代码文件,并向 NodeChildrenRequested 和 CreateWebPartNodes 方法的第一个代码行中添加一个断点。
按 F5 开始调试。
Visual Studio 将扩展安装到 %UserProfile%\AppData\Local\Microsoft\VisualStudio\10.0Exp\Extensions\Contoso\Web Part Gallery Node Extension for Server Explorer\1.0 中,并启动 Visual Studio 的实验实例。 您将在此 Visual Studio 实例中测试项目项。
测试扩展
在 Visual Studio 的实验实例中,在**“视图”菜单上单击“服务器资源管理器”**。
验证要用于测试的 SharePoint 网站是否显示在**“服务器资源管理器”中的“SharePoint 连接”**节点下。 如果未列出此网站,请执行下列步骤:
右击**“SharePoint 连接”并单击“添加连接”**。
在**“添加 SharePoint 连接”**对话框中,输入要连接到的 SharePoint 网站的 URL。 若要指定开发计算机上的 SharePoint 网站,请键入 https://localhost。
单击**“确定”**。
展开网站集节点(即显示网站的 URL 的节点),再展开子网站节点(例如**“团队网站”**)。
验证另一个 Visual Studio 实例中的代码是否会在您之前在 NodeChildrenRequested 方法中设置的断点处停止。 按 F5 以继续调试项目。
在 Visual Studio 的实验实例中,验证名为**“Web 部件库”的新节点是否显示在首要网站节点下。 展开“Web 部件库”**节点。
验证另一个 Visual Studio 实例中的代码是否会在您之前在 CreateWebPartNodes 方法中设置的断点处停止。 按 F5 以继续调试项目。
在 Visual Studio 的实验实例中,验证已连接网站上的所有 Web 部件是否都显示在**“服务器资源管理器”中的“Web 部件库”**节点下。
右击其中的某个 Web 部件,然后单击**“属性”**。
验证有关 Web 部件的详细信息是否显示在**“属性”**窗口中。
在**“服务器资源管理器”中,再次右击同一个 Web 部件,然后单击“显示消息”**。
验证是否出现消息框。 在消息框中单击**“确定”**。
从 Visual Studio 中卸载扩展
测试完扩展后,请从 Visual Studio 卸载扩展。
卸载扩展
在 Visual Studio 的实验实例中,在**“工具”菜单上单击“扩展管理器”**。
这将打开**“扩展管理器”**对话框。
在扩展列表中,单击“服务器资源管理器的 Web 部件库节点扩展”,然后单击**“卸载”**。
在出现的对话框中,单击**“是”**以确认您要卸载该扩展。
单击**“立即重新启动”**以完成卸载。 相应的项目项也会随之卸载。
关闭 Visual Studio 的两个实例(Visual Studio 的实验实例和 Visual Studio 的已打开 WebPartNode 解决方案的实例)。