演练:创建 SharePoint 项目扩展

本演练阐释如何创建 SharePoint 项目的扩展。向项目中添加,删除或重命名时,可以使用项目扩展响应项目级别事件 (例如)。还可以添加自定义属性或在某个属性值更改时做出响应。与项目项扩展不同,无法将项目扩展与特定的 SharePoint 项目类型相关联。创建项目扩展后,当在 Visual Studio 中打开任何类型的 SharePoint 项目时,此扩展都会加载。

在本演练中,您将创建一个将添加到 Visual Studio 中创建的任何 SharePoint 项目中的自定义布尔属性。如果设置为 True,则新属性会将 Images 资源文件夹添加或映射到您的项目。如果设置为 False,则将删除 Images 文件夹(如果存在)。有关更多信息,请参见如何:添加和移除映射文件夹

本演练将演示以下任务:

  • 为 SharePoint 项目创建可执行以下操作的 Visual Studio 扩展:

    • 将自定义项目属性添加到“属性”窗口。该属性将应用于任何 SharePoint 项目。

    • 使用 SharePoint 项目对象模型将映射文件夹添加到项目中。

    • 使用 Visual Studio 自动化对象模型 (DTE) 从项目中删除映射文件夹。

  • 生成 Visual Studio 扩展 (VSIX) 包以部署项目属性的扩展程序集。

  • 调试并测试项目属性。

系统必备

您需要在开发计算机上安装以下组件才能完成本演练:

创建项目

若要完成本演练,您必须创建以下两个项目:

  • 一个用于创建 VSIX 包以部署项目扩展的 VSIX 项目。

  • 一个用于实现项目扩展的类库项目。

从创建项目开始本演练。

创建 VSIX 项目

  1. 启动 Visual Studio。

  2. 在菜单栏上,选择**“文件”“新建**、“项目”

  3. 新建项目 对话框中,展开 visual C#Visual Basic 节点,然后选择 扩展性 节点。

    说明说明

    只有在安装 Visual Studio SDK,此节点可用。有关更多信息,请参见本主题前面的系统必备部分。

  4. 在对话框顶部,选择在 .NET Framework 的版本列表的 .NET Framework 4.5,然后选择 VSIX 项目 模板。

  5. 名称 框中,输入 ProjectExtensionPackage,然后选择 确定 按钮。

    ProjectExtensionPackage 项显示在 解决方案资源管理器

创建扩展项目

  1. 解决方案资源管理器,请打开解决方案节点的快捷菜单上,选择 添加,然后选择 新建项目

    说明说明

    在 Visual Basic 项目,因此,只有当 总是显示解决方案 复选框。General, Projects and Solutions, Options Dialog Box,已选择解决方案节点显示在 解决方案资源管理器

  2. 新建项目 对话框中,展开 visual C#Visual Basic 节点,然后选择 Windows

  3. 在对话框顶部,选择在 .NET Framework 的版本列表的 .NET Framework 4.5,然后选择 类库 项目模板。

  4. 名称 框中,输入 ProjectExtension,然后选择 确定 按钮。

    Visual Studio 将**“ProjectExtension”**项目添加到解决方案中,并打开默认的 Class1 代码文件。

  5. 从项目中删除 Class1 代码文件。

配置项目

在编写代码以创建项目扩展之前,请将代码文件和程序集引用添加到扩展项目中。

配置项目

  1. 添加一个名为" ProjectExtension 项目的 CustomProperty 的代码文件。

  2. 打开 ProjectExtension 项目的快捷菜单,然后选择 添加引用

  3. 引用管理器– CustomProperty 对话框中,选择 框架 节点,请在 System.ComponentModel.Composition 和 System.Windows.Forms 程序集例旁边的复选框。

  4. 选择 扩展 节点,请在 Microsoft.VisualStudio.SharePoint 和 EnvDTE 程序集旁边的复选框,然后选择 确定 按钮。

  5. 解决方案资源管理器,在 ProjectExtension 项目的 引用 文件夹下,选择 EnvDTE

  6. 在**“属性”窗口中,将“嵌入互操作类型”属性更改为“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;
    
            }
        }
    }
    

生成解决方案

接下来,生成解决方案以确保编译时不会出错。

生成解决方案

  • 在菜单栏上,依次选择 Build生成解决方案

创建 VSIX 包以部署项目属性扩展

若要部署项目扩展,请使用解决方案中的 VSIX 项目来创建 VSIX 包。首先,通过修改 VSIX 项目中包含的 source.extension.vsixmanifest 文件来配置 VSIX 包。然后,通过生成解决方案来创建 VSIX 包。

配置并创建 VSIX 包

  1. 解决方案资源管理器,打开 source.extension.vsixmanifest 文件的快捷菜单,然后选择 打开 按钮。

    Visual Studio 打开在清单设计器中的文件。也会出现在 元数据 选项的信息显示在 扩展和更新。所有 VSIX 包必需的 extension.vsixmanifest 文件。有关此文件的更多信息,请参见VSIX 扩展架构参考

  2. 产品名称 框中,输入 自定义项目属性。

  3. 作者 框中,输入 Contoso。

  4. 说明 框中,输入 切换映射 images 资源文件夹添加到项目中的自定义 SharePoint 项目属性。

  5. 选择 资产 选项卡,然后选择 新建 按钮。

    添加新资产 出现对话框。

  6. 类型 列表中,选择 Microsoft.VisualStudio.MefComponent

    说明说明

    此值对应于 extension.vsixmanifest 文件中的 MEFComponent 元素。此元素指定 VSIX 包中的扩展程序集的名称。有关更多信息,请参见MEFComponent Element

  7. 列表中,选择 当前解决方案中的项目 选项按钮。

  8. 项目 列表中,选择 ProjectExtension。

    此值标识程序集的名称在项目中生成。

  9. 选择 确定 关闭 添加新资产 对话框。

  10. 在菜单栏上,依次选择 文件全部保存,完成后,然后关闭清单设计器。

  11. 在菜单栏上,依次选择 Build生成解决方案,然后确保项目在编译时不会出错。

  12. 解决方案资源管理器,打开 ProjectExtensionPackage 项目的快捷菜单,并选择 在文件资源管理器中打开文件夹 按钮。

  13. 文件资源管理器,请打开 ProjectExtensionPackage 项目的生成输出文件夹,然后验证该文件夹包含名为 ProjectExtensionPackage.vsix 的文件。

    默认情况下,生成输出文件夹为包含项目文件的文件夹下的 ..\bin\Debug 文件夹。

测试项目属性

现在已准备好测试自定义项目属性。调试和测试在 Visual Studio的实验实例的新项目属性扩展最为简单。当您运行 VSIX 或其他扩展性项目时,Visual Studio 此创建实例。在调试项目后,您的系统上安装该扩展然后继续调试和测试它在 Visual Studio常规实例。

在 Visual Studio 的实验实例中调试和测试扩展

  1. 重新启动使用管理凭据的 Visual Studio,然后打开 ProjectExtensionPackage 解决方案。

  2. 通过选择 F5 键启动项目的调试版本或,在菜单栏上,选择 调试启动调试

    Visual Studio 将扩展安装到 %UserProfile% \ AppData \ local \ Microsoft \ VisualStudio \ 11.0Exp \ extensions \ Contoso \ custom project property \ 1.0 中启动 Visual Studio的实验实例。

  3. 在 Visual Studio的实验实例中,创建场解决方案的 SharePoint 项目,并为其他值使用默认值在向导。

    1. 在菜单栏上,选择**“文件”“新建**、“项目”

    2. 新建项目 对话框的顶部,选择在 .NET Framework 的版本列表的 .NET Framework 3.5

      SharePoint 工具扩展需要在 .NET Framework的这一版本的功能。

    3. 模板 节点下,展开 visual C#Visual Basic 节点,选择 SharePoint 节点,然后选择 2010 节点。

    4. 选择 SharePoint 2010 项目 模板,然后转到 ModuleTest 是项目的名称。

  4. 解决方案资源管理器,选择 ModuleTest 项目节点。

    “属性”窗口中将显示新的自定义属性“映射 Images 文件夹”,其默认值为 False。

  5. 更改该属性的值更改为 True。

    Images 资源文件夹将添加到 SharePoint 项目中。

  6. 更改该属性的值重 False。

    如果选择在 删除 images 文件夹? 对话框的 按钮,images 资源文件夹从 SharePoint 项目中删除。

  7. 关闭 Visual Studio 的实验实例。

请参见

概念

扩展 SharePoint 项目

如何:向 SharePoint 项目中添加属性

在 SharePoint 项目系统类型和其他 Visual Studio 项目类型之间进行转换

在 SharePoint 项目系统的扩展中保存数据

将自定义数据与 SharePoint 工具扩展相关联