Tutorial: Crear una extensión de proyecto de SharePoint

En este tutorial se muestra cómo crear una extensión para los proyectos de SharePoint. Puede utilizar una extensión de proyecto para responder a los eventos de nivel de proyecto, como cuando se agrega o se elimina un proyecto, o se le cambia de nombre. También puede agregar propiedades personalizadas o responder cuando cambia un valor de propiedad. A diferencia de las extensiones de elemento de proyecto, las extensiones de proyecto no pueden estar asociadas a un tipo de proyecto de SharePoint determinado. Cuando crea una extensión de proyecto, se carga cuando se abre cualquier tipo de proyecto de SharePoint en Visual Studio.

En este tutorial, creará una propiedad booleana personalizada que se agrega a cualquier proyecto de SharePoint creado en Visual Studio. Cuando se establece en True, la nueva propiedad agrega o asigna una carpeta de recursos Images al proyecto. Cuando se establece en False, se quita la carpeta Images, si existe. Para obtener más información, vea Cómo: Agregar y quitar carpetas asignadas.

En este tutorial se muestran las siguientes tareas:

  • Crear una extensión Visual Studio para los proyectos SharePoint que hace lo siguiente:

    • Agrega una propiedad de proyecto personalizada a la ventana Propiedades. La propiedad se aplica a cualquier proyecto de SharePoint.

    • Utiliza el modelo de objetos de proyecto de SharePoint para agregar una carpeta asignada a un proyecto.

    • Usa el modelo de objetos de automatización (DTE) de Visual Studio para eliminar una carpeta asignada del proyecto.

  • Compilar un paquete de extensión (VSIX) de Visual Studio para implementar el ensamblado de la extensión de propiedad de proyecto.

  • Depurar y probar la propiedad de proyecto.

Requisitos previos

Necesitará los componentes siguientes en el equipo de desarrollo para completar este tutorial:

Crear los proyectos

Para completar este tutorial, debe crear dos proyectos:

  • Un proyecto VSIX para crear el paquete VSIX e implementar la extensión de proyecto.

  • Un proyecto de biblioteca de clases que implemente la extensión de proyecto.

Comience el tutorial creando ambos proyectos.

Para crear el proyecto de VSIX

  1. Inicie Visual Studio.

  2. En el menú Archivo, elija Nuevo y haga clic en Proyecto.

  3. En el cuadro de diálogo Nuevo proyecto, expanda el nodo Visual Basic o Visual C# y, a continuación, haga clic en el nodo Extensibilidad.

    Nota

    El nodo Extensibilidad solo está disponible si instala Visual Studio 2010 SDK. Para obtener más información, vea la sección Requisitos previos, anteriormente en este tutorial.

  4. En el cuadro combinado de la parte superior del cuadro de diálogo, seleccione .NET Framework 4. Las extensiones de herramientas de SharePoint requieren características de .NET Framework.

  5. Haga clic en la plantilla Proyecto VSIX.

  6. En el cuadro Nombre, escriba ProjectExtensionPackage.

  7. Haga clic en Aceptar.

    Visual Studio agrega el proyecto ProjectExtensionPackage al Explorador de soluciones.

Para crear la extensión de proyecto

  1. En el Explorador de soluciones, haga clic con el botón secundario en el nodo de la solución, después haga clic en Agregar y, a continuación, en Nuevo proyecto.

    Nota

    En los proyectos de Visual Basic, el nodo de la solución aparece en el Explorador de soluciones solo cuando se activa la casilla Mostrar solución siempre en General, Proyectos y soluciones, Opciones (Cuadro de diálogo).

  2. En el cuadro de diálogo Nuevo proyecto, expanda Visual C# o Visual Basic y, a continuación, haga clic en Windows.

  3. En el cuadro combinado de la parte superior del cuadro de diálogo, seleccione .NET Framework 4.

  4. Seleccione la plantilla de proyecto Biblioteca de clases.

  5. En el cuadro Nombre, escriba ProjectExtension.

  6. Haga clic en Aceptar.

    Visual Studio agrega el proyecto ProjectExtension a la solución y abre el archivo de código predeterminado Class1.

  7. Elimine el archivo de código Class1 del proyecto.

Configurar el proyecto

Antes de escribir el código que crea la extensión del elemento de proyecto, tiene que agregar los archivos de código y las referencias de ensamblado al proyecto de extensión.

Para configurar el proyecto

  1. Agregue un nuevo archivo de código denominado CustomProperty al proyecto ProjectExtension.

  2. Haga clic en la opción Agregar referencia del menú Proyecto.

  3. En la pestaña .NET, presione CTRL y haga clic en los ensamblados siguientes; a continuación, haga clic en Aceptar:

    • Microsoft.VisualStudio.SharePoint

    • System.ComponentModel.Composition

    • System.Windows.Forms

    • EnvDTE

  4. En el Explorador de soluciones, en la carpeta Referencias del proyecto ProjectExtension, haga clic en EnvDTE.

  5. En la ventana Propiedades, cambie el valor de Incrustar tipos de interoperabilidad a False.

Definir la nueva propiedad de proyecto de SharePoint

Cree una clase que defina la extensión de proyecto y el comportamiento de la nueva propiedad. Para definir la nueva extensión de proyecto, la clase implementa la interfaz ISharePointProjectExtension. Implemente esta interfaz siempre que desee definir una extensión para un proyecto de SharePoint. También agregue ExportAttribute a la clase. Este atributo permite que Visual Studio detecte y cargue la implementación de ISharePointProjectExtension. Pase el tipo ISharePointProjectExtension al constructor del atributo.

Para definir la nueva propiedad de proyecto de SharePoint

  1. Haga doble clic en el archivo de código CustomProperty para modificarlo, si no está abierto.

  2. Pegue el código siguiente en el archivo.

    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;
    
            }
        }
    }
    

Compilar la solución

A continuación, compile la solución para asegurarse de que se compila sin errores.

Para compilar la solución

  • En el menú Generar, haga clic en Generar solución.

Crear un paquete VSIX para implementar la extensión de propiedad de proyecto

Para implementar la extensión de proyecto, utilice el proyecto VSIX en la solución para crear un paquete VSIX. Primero, configure el paquete VSIX modificando el archivo source.extension.vsixmanifest incluido en el proyecto VSIX. A continuación, cree el paquete VSIX compilando la solución.

Para crear y configurar el paquete VSIX

  1. En el Explorador de soluciones, haga doble clic en el archivo source.extension.vsixmanifest.

    Visual Studio abre el archivo en el editor de manifiestos. Este editor proporciona una UI que puede utilizar para modificar el XML del manifiesto. Esta información se muestra más adelante en el Administrador de extensiones. Todos los paquetes VSIX requieren el archivo extension.vsixmanifest. Para obtener más información sobre este archivo, vea VSIX Extension Schema Reference.

  2. En el cuadro Nombre de producto, escriba Custom Project Property.

  3. En el cuadro Autor, escriba Contoso.

  4. En el cuadro Descripción, escriba Una propiedad de proyecto de SharePoint personalizada que alterna la asignación de la carpeta de recurso Images al proyecto.

  5. En la sección Contenido del editor, haga clic en el botón Agregar contenido.

  6. Seleccione MEF Component en el cuadro desplegable Seleccione un tipo de contenido.

    Nota

    Este valor corresponde al elemento MEFComponent del archivo extension.vsixmanifest. Este elemento especifica el nombre de un ensamblado de extensión en el paquete VSIX. Para obtener más información, vea MEFComponent Element (VSX Schema).

  7. En la sección Seleccionar un origen, haga clic en la opción Proyecto y, a continuación, seleccione ProjextExtension en el cuadro desplegable.

    Este valor identifica el nombre del ensamblado que está compilando en el proyecto.

  8. Cuando termine, haga clic en Aceptar para cerrar el cuadro de diálogo Agregar contenido.

  9. Cuando haya finalizado, haga clic en Guardar todo en el menú Archivo y, a continuación, cierre el diseñador del manifiesto.

  10. En el menú Generar, haga clic en Generar solución. Asegúrese de que el proyecto se compila sin errores.

  11. Haga clic en el proyecto ProjectExtensionPackage en el Explorador de soluciones, haga clic en el botón Mostrar todos los archivos y, a continuación, abra la carpeta de resultado de compilación de ProjectExtensionPackage. Esta carpeta debería contener un archivo ProjectExtensionPackage.vsix.

    De forma predeterminada, la carpeta de resultado de compilación es .. \bin\Debug, que se encuentra bajo la carpeta que contiene el archivo de proyecto.

Probar la propiedad de proyecto

Ya puede probar la propiedad de proyecto personalizada. Es más fácil depurar y probar la nueva extensión de propiedad de proyecto en una instancia experimental de Visual Studio. Esta es la instancia de Visual Studio que se crea al ejecutar un VSIX u otro proyecto de extensibilidad. Una vez depurado el proyecto, puede instalar la extensión en su sistema y seguir depurándola y probándola en una instancia normal de Visual Studio.

Para depurar y probar la extensión en una instancia experimental de Visual Studio

  1. Reinicie Visual Studio con credenciales administrativas y abra la solución ProjectExtensionPackage.

  2. Presione F5 para iniciar una compilación de depuración del proyecto.

    Visual Studio instala la extensión en %UserProfile%\AppData\Local\Microsoft\VisualStudio\10.0Exp\Extensions\Contoso\Custom Project Property\1 .0 e inicia una instancia experimental de Visual Studio.

  3. En la instancia experimental de Visual Studio, cree nuevo proyecto de SharePoint de una solución de granja de servidores, como un Módulo. Use los valores predeterminados en los demás valores del asistente.

  4. Haga clic en el nodo del proyecto en el Explorador de soluciones.

    Una nueva propiedad Map Images Folder personalizado aparece en la ventana Propiedades con un valor predeterminado de False.

  5. Cambie Map Images Folder a True.

    Se agrega una carpeta de recurso Images al proyecto SharePoint.

  6. Cambie Map Images Folder a False.

    La carpeta de recurso Images se elimina del proyecto SharePoint.

  7. Cierre la instancia experimental de Visual Studio.

Vea también

Otros recursos

Extender los proyectos de SharePoint

Cómo: Agregar una propiedad a proyectos de SharePoint

Convertir los tipos de sistema de proyectos de SharePoint en otros tipos de proyecto de Visual Studio

Guardar datos asociados en extensiones del sistema de proyectos de SharePoint

Asociar datos personalizados con extensiones de herramientas de SharePoint