本演练阐释如何创建 SharePoint 项目的扩展。 可以使用项目扩展来响应项目级事件,例如,在添加、删除项目或更改项目名称时。 还可以添加自定义属性或在某个属性值更改时做出响应。 与项目项扩展不同,无法将项目扩展与特定的 SharePoint 项目类型相关联。 创建项目扩展后,当在 Visual Studio 中打开任何类型的 SharePoint 项目时,此扩展都会加载。
在本演练中,您将创建一个将添加到 Visual Studio 中创建的任何 SharePoint 项目中的自定义布尔属性。 如果设置为 True,则新属性会将 Images 资源文件夹添加或映射到您的项目。 如果设置为 False,则将删除 Images 文件夹(如果存在)。 有关更多信息,请参见如何:添加和移除映射文件夹。
本演练将演示以下任务:
为 SharePoint 项目创建可执行以下操作的 Visual Studio 扩展:
将自定义项目属性添加到“属性”窗口。 该属性将应用于任何 SharePoint 项目。
使用 SharePoint 项目对象模型将映射文件夹添加到项目中。
使用 Visual Studio 自动化对象模型 (DTE) 从项目中删除映射文件夹。
生成 Visual Studio 扩展 (VSIX) 包以部署项目属性的扩展程序集。
调试并测试项目属性。
系统必备
您需要在开发计算机上安装以下组件才能完成本演练:
支持的 Microsoft Windows、SharePoint 和 Visual Studio 版本。 有关更多信息,请参见开发 SharePoint 解决方案的要求。
Visual Studio 2010 SDK。 本演练使用 SDK 中的**“VSIX 项目”**模板来创建 VSIX 包以部署项目属性扩展。 有关更多信息,请参见扩展 Visual Studio 中的 SharePoint 工具。
创建项目
若要完成本演练,您必须创建以下两个项目:
一个用于创建 VSIX 包以部署项目扩展的 VSIX 项目。
一个用于实现项目扩展的类库项目。
从创建项目开始本演练。
创建 VSIX 项目
启动 Visual Studio。
在**“文件”菜单上指向“新建”,再单击“项目”**。
在**“新建项目”对话框中,展开“Visual C#”或“Visual Basic”节点,然后单击“扩展性”**节点。
提示
只有在安装 Visual Studio 2010 SDK 之后,“扩展性”节点才可用。 有关更多信息,请参见本主题前面的系统必备部分。
在对话框顶部的组合框中,选择**“.NET Framework 4”**。 SharePoint 工具扩展需要此版本的 .NET Framework 中的功能。
单击**“VSIX 项目”**模板。
在**“名称”**框中键入 ProjectExtensionPackage。
单击**“确定”**。
Visual Studio 会将**“ProjectExtensionPackage”项目添加到“解决方案资源管理器”**中。
创建扩展项目
在**“解决方案资源管理器”中,右击解决方案节点,单击“添加”,再单击“新建项目”**。
提示
在 Visual Basic 项目中,仅当在“选项”对话框 ->“项目和解决方案”->“常规”中选中“总是显示解决方案”复选框时,解决方案节点才会出现在“解决方案资源管理器”中。
在**“新建项目”对话框中,展开“Visual C#”或“Visual Basic”节点,然后单击“Windows”**。
在对话框顶部的组合框中,选择**“.NET Framework 4”**。
选择**“类库”**项目模板。
在**“名称”**框中,键入 ProjectExtension。
单击**“确定”**。
Visual Studio 将**“ProjectExtension”**项目添加到解决方案中,并打开默认的 Class1 代码文件。
从项目中删除 Class1 代码文件。
配置项目
在编写代码以创建项目扩展之前,请将代码文件和程序集引用添加到扩展项目中。
配置项目
将名为 CustomProperty 的新代码文件添加到 ProjectExtension 项目中。
在**“项目”菜单上,单击“添加引用”**。
在**“.NET”选项卡上,按住 Ctrl 的同时单击下列程序集,然后单击“确定”**:
Microsoft.VisualStudio.SharePoint
System.ComponentModel.Composition
System.Windows.Forms
EnvDTE
在**“解决方案资源管理器”中,在 ProjectExtension 项目的“引用”文件夹下,单击“EnvDTE”**。
在**“属性”窗口中,将“嵌入互操作类型”属性更改为“False”**。
定义新的 SharePoint 项目属性
创建一个定义项目扩展和新项目属性的行为的类。 若要定义新的项目扩展,此类应实现 ISharePointProjectExtension 接口。 每当需要定义 SharePoint 项目扩展时,就要实现此接口。 另外,将 ExportAttribute 添加到此类中。 此特性使 Visual Studio 能够发现和加载您的 ISharePointProjectExtension 实现。 将 ISharePointProjectExtension 类型传递给特性的构造函数。
定义新的 SharePoint 项目属性
如果 CustomProperty 代码文件尚未打开,请双击此文件进行编辑。
将下面的代码粘贴到此文件中。
Imports System Imports System.Linq Imports System.ComponentModel Imports System.ComponentModel.Composition Imports System.Windows.Forms Imports Microsoft.VisualStudio.SharePoint Imports EnvDTE Namespace Contoso.SharePointProjectExtensions.MapImagesFolder ' Export attribute: Enables Visual Studio to discover and load this extension. ' MapImagesFolderProjectExtension class: Adds a new Map Images Folder property to any SharePoint project. <Export(GetType(ISharePointProjectExtension))> _ Public Class MapImagesFolderProjectExtension Implements ISharePointProjectExtension Public Sub Initialize(ByVal projectService As ISharePointProjectService) Implements ISharePointProjectExtension.Initialize AddHandler projectService.ProjectPropertiesRequested, AddressOf Me.projectService_ProjectPropertiesRequested End Sub Private Sub projectService_ProjectPropertiesRequested(ByVal sender As Object, ByVal e As SharePointProjectPropertiesRequestedEventArgs) Dim propertiesObject As CustomProjectProperties = Nothing ' If the properties object already exists, get it from the project's annotations. If False = e.Project.Annotations.TryGetValue(propertiesObject) Then ' Otherwise, create a new properties object and add it to the annotations. propertiesObject = New CustomProjectProperties(e.Project) e.Project.Annotations.Add(propertiesObject) End If e.PropertySources.Add(propertiesObject) End Sub End Class Public Class CustomProjectProperties Private sharePointProject As ISharePointProject = Nothing Private Const MapImagesFolderPropertyDefaultValue As Boolean = False Private Const MapImagesFolderPropertyId = "ContosoMapImagesFolderProperty" Public Sub New(ByVal myProject As ISharePointProject) sharePointProject = myProject End Sub ' Represents the new boolean property MapImagesFolder. ' True = Map an Images folder to the project if one does not already exist; otherwise, do nothing. ' False = Remove the Images folder from the project, if one exists; otherwise, do nothing. <DisplayName("Map Images Folder")> _ <DescriptionAttribute("Specifies whether an Images folder is mapped to the SharePoint project.")> _ <DefaultValue(MapImagesFolderPropertyDefaultValue)> _ Public Property MapImagesFolder As Boolean Get Dim propertyStringValue As String = String.Empty ' Try to get the current value from the .user file; if it does not yet exist, return a default value. If Not sharePointProject.ProjectUserFileData.TryGetValue(MapImagesFolderPropertyId, propertyStringValue) Then Return MapImagesFolderPropertyDefaultValue Else Return CBool(propertyStringValue) End If End Get Set(ByVal value As Boolean) If value Then If Not ImagesMappedFolderInProjectExists(sharePointProject) Then ' An Images folder is not mapped to the project, so map one. Dim mappedFolder As IMappedFolder = sharePointProject.MappedFolders.Add(MappedFolderType.Images) sharePointProject.ProjectService.Logger.WriteLine( _ mappedFolder.Name & " mapped folder added to the project.", LogCategory.Status) End If ElseIf (ImagesMappedFolderInProjectExists(sharePointProject) AndAlso UserSaysDeleteFile()) Then ' An Images folder is mapped to the project and the user wants to remove it. DeleteFolder() End If sharePointProject.ProjectUserFileData(MapImagesFolderPropertyId) = value.ToString() End Set End Property Private Function ImagesMappedFolderInProjectExists(ByVal sharePointProject As ISharePointProject) As Boolean Dim returnValue As Boolean = False For Each folder As IMappedFolder In sharePointProject.MappedFolders ' Check to see if an Images folder is already mapped. If (folder.FolderType = MappedFolderType.Images) Then returnValue = True End If Next Return returnValue End Function Private Function UserSaysDeleteFile() As Boolean ' Ask the user whether they want to delete the Images folder. Dim returnValue As Boolean = False If (MessageBox.Show("Do you want to delete the Images folder from the project?", _ "Delete the Images folder?", MessageBoxButtons.YesNo) = DialogResult.Yes) Then returnValue = True End If Return returnValue End Function Private Sub DeleteFolder() ' The Visual Studio DTE object model is required to delete the mapped folder. Dim dteProject As EnvDTE.Project = _ sharePointProject.ProjectService.Convert(Of ISharePointProject, EnvDTE.Project)(sharePointProject) Dim targetFolderName As String = _ sharePointProject.MappedFolders.First(Function(mf) mf.FolderType = MappedFolderType.Images).Name Dim mappedFolderItem As EnvDTE.ProjectItem = dteProject.ProjectItems.Item(targetFolderName) mappedFolderItem.Delete() sharePointProject.ProjectService.Logger.WriteLine("Mapped Folder " & _ targetFolderName & " deleted", LogCategory.Status) End Sub End Class End Namespace
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.ComponentModel.Composition; using System.Windows.Forms; using Microsoft.VisualStudio.SharePoint; using EnvDTE; // Adds a new property called MapImagesFolder to any SharePoint project. // When MapImagesFolder is set to true, the Image folder is mapped to the project. // When MapImagesFolder is set to false, the Image folder is deleted from the project. namespace SP_Project_Extension { // Export attribute: Enables Visual Studio to discover and load this extension. [Export(typeof(ISharePointProjectExtension))] // Defines a new custom project property that applies to any SharePoint project. public class SPProjectExtension : ISharePointProjectExtension { // Implements ISharePointProjectService.Initialize, which determines the behavior of the new property. public void Initialize(ISharePointProjectService projectService) { // Handle events for when a project property is changed. projectService.ProjectPropertiesRequested += new EventHandler<SharePointProjectPropertiesRequestedEventArgs>(projectService_ProjectPropertiesRequested); } void projectService_ProjectPropertiesRequested(object sender, SharePointProjectPropertiesRequestedEventArgs e) { // Add a new property to the SharePoint project. e.PropertySources.Add((object)new ImagesMappedFolderProperty(e.Project)); } } public class ImagesMappedFolderProperty { ISharePointProject sharePointProject = null; public ImagesMappedFolderProperty(ISharePointProject myProject) { sharePointProject = myProject; } static bool MapFolderSetting = false; [DisplayName("Map Images Folder")] [DescriptionAttribute("Specifies whether an Images folder is mapped to the SharePoint project.")] public bool MapImagesFolder // Represents the new boolean property MapImagesFolder. // True = Map an Images folder to the project if one does not already exist; otherwise, do nothing. // False = Remove the Images folder from the project, if one exists; otherwise, do nothing. { get { // Get the current property value. return MapFolderSetting; } set { if (value) { if (!ImagesMappedFolderInProjectExists(sharePointProject)) { // An Images folder is not mapped to the project, so map one. IMappedFolder mappedFolder1 = sharePointProject.MappedFolders.Add(MappedFolderType.Images); // Add a note to the logger that a mapped folder was added. sharePointProject.ProjectService.Logger.WriteLine("Mapped Folder added:" + mappedFolder1.Name, LogCategory.Status); } } else { if (ImagesMappedFolderInProjectExists(sharePointProject) && UserSaysDeleteFile()) { // An Images folder is mapped to the project and the user wants to remove it. // The Visual Studio DTE object model is required to delete the mapped folder. // Reference the Visual Studio DTE model, get handles for the SharePoint project and project items. EnvDTE.Project dteProject = sharePointProject.ProjectService.Convert<ISharePointProject, EnvDTE.Project>(sharePointProject); string targetFolderName = sharePointProject.MappedFolders.First(mf => mf.FolderType == MappedFolderType.Images).Name; EnvDTE.ProjectItem mappedFolderItem = dteProject.ProjectItems.Item(targetFolderName); mappedFolderItem.Delete(); sharePointProject.ProjectService.Logger.WriteLine("Mapped Folder " + targetFolderName + " deleted", LogCategory.Status); } } MapFolderSetting = value; } } private bool ImagesMappedFolderInProjectExists(ISharePointProject sharePointProject) { bool retVal = false; foreach (IMappedFolder folder in sharePointProject.MappedFolders) { // Check to see if an Images folder is already mapped. if (folder.FolderType == MappedFolderType.Images) retVal = true; } return retVal; } private bool UserSaysDeleteFile() { // Prompt the user whether they want to delete the Images folder. bool retVal = false; if (MessageBox.Show("Do you want to delete the Images folder from the project?", "Delete the Images folder?", MessageBoxButtons.YesNo) == DialogResult.Yes) { retVal = true; } return retVal; } } }
生成解决方案
接下来,生成解决方案以确保编译时不会出错。
生成解决方案
- 在**“生成”菜单上,单击“生成解决方案”**。
创建 VSIX 包以部署项目属性扩展
若要部署项目扩展,请使用解决方案中的 VSIX 项目来创建 VSIX 包。 首先,通过修改 VSIX 项目中包含的 source.extension.vsixmanifest 文件来配置 VSIX 包。 然后,通过生成解决方案来创建 VSIX 包。
配置并创建 VSIX 包
在**“解决方案资源管理器”中,双击“source.extension.vsixmanifest”**文件。
Visual Studio 将在清单编辑器中打开该文件。 此编辑器提供可用于编辑清单中的 XML 的 UI。 此信息稍后会显示在**“扩展管理器”**中。所有 VSIX 包都需要 extension.vsixmanifest 文件。 有关此文件的更多信息,请参见VSIX Extension Schema Reference。
在**“产品名称”**框中键入“自定义项目属性”。
在**“作者”**框中键入 Contoso。
在**“说明”**框中,键入“用于将 Images 资源文件夹的映射切换到项目的自定义 SharePoint 项目属性”。
在编辑器的**“内容”部分中,单击“添加内容”**按钮。
在**“选择内容类型”下拉框中选择“MEF 组件”**。
提示
此值对应于 extension.vsixmanifest 文件中的 MEFComponent 元素。 此元素指定 VSIX 包中的扩展程序集的名称。 有关更多信息,请参见 MEFComponent Element (VSX Schema)。
在**“选择源”部分中,单击“项目”**选项,然后在下拉框中选择“ProjextExtension”。
此值标识您在项目中生成的程序集的名称。
完成后,单击**“确定”以关闭“添加内容”**对话框。
完成后,单击**“文件”菜单上的“全部保存”**,然后关闭清单设计器。
在**“生成”菜单上,单击“生成解决方案”**。 确保项目在编译时不会出错。
在**“解决方案资源管理器”中单击“ProjectExtensionPackage”项目,单击“显示所有文件”**按钮,然后打开 ProjectExtensionPackage 项目的生成输出文件夹。 此文件夹现在应包含一个名为 ProjectExtensionPackage.vsix 的文件。
默认情况下,生成输出文件夹为 包含项目文件的文件夹下的 ..\bin\Debug 文件夹。
测试项目属性
现在您可以对自定义项目属性进行测试了。 在 Visual Studio 的实验实例中调试和测试新的项目属性扩展最为简单。 这是您在运行 VSIX 或其他扩展性项目时创建的 Visual Studio 实例。 调试项目后,您可以将扩展安装在系统上,然后在 Visual Studio 的常规实例中继续调试和测试扩展。
在 Visual Studio 的实验实例中调试和测试扩展
利用管理凭据重新启动 Visual Studio,然后打开 ProjectExtensionPackage 解决方案。
按**“F5”**以启动项目的调试版本。
Visual Studio 将扩展安装到 %UserProfile%\AppData\Local\Microsoft\VisualStudio\10.0Exp\Extensions\Contoso\Custom Project Property\1.0 中,并启动 Visual Studio 的实验实例。
在 Visual Studio 的实验实例中,创建一个新的场解决方案 SharePoint 项目,例如一个模块。 对向导中的其他值使用默认值。
在**“解决方案资源管理器”**中单击项目节点。
“属性”窗口中将显示新的自定义属性“映射 Images 文件夹”,其默认值为 False。
将**“映射 Images 文件夹”**更改为 True。
Images 资源文件夹将添加到 SharePoint 项目中。
将**“映射 Images 文件夹”**更改为 False。
将从 SharePoint 项目中删除 Images 资源文件夹。
关闭 Visual Studio 的实验实例。