Tutorial: Crear un elemento de proyecto de columna de sitio con una plantilla de proyecto, parte 2

Después de definir un tipo personalizado de elemento de proyecto de SharePoint y asociarlo a una plantilla de proyecto en Visual Studio, es posible que desee proporcionar también un asistente para la plantilla. Puede usar el asistente con el fin de recopilar información de los usuarios cuando usan la plantilla para crear un nuevo proyecto que contiene el elemento de proyecto. La información que recopile puede usarse para inicializar el elemento de proyecto.

En este tutorial, agregará un asistente a la plantilla de proyecto de columnas de sitio que se muestra en Tutorial: Crear un elemento de proyecto de columna de sitio con una plantilla de proyecto, parte 1. Cuando un usuario crea un proyecto de columna de sitio, el asistente recopila información sobre la columna de sitio (como el tipo base y el grupo) y agrega esta información al archivo Elements.xml del nuevo proyecto.

En este tutorial se muestran las siguientes tareas:

  • Crear un asistente para un tipo de elemento de proyecto de SharePoint personalizado asociado a una plantilla de proyecto.

  • Definir una interfaz de usuario de asistente personalizada similar a la de los asistentes integrados para los proyectos de SharePoint en Visual Studio 2010.

  • Crear dos comandos de SharePoint que se usan para llamar al sitio de SharePoint local mientras el asistente se está ejecutando. Los comandos de SharePoint son métodos que las extensiones de Visual Studio pueden usar para llamar a las API del modelo de objetos de servidor de SharePoint. Para obtener más información, vea Llamar a los modelos de objetos de SharePoint.

  • Usar parámetros reemplazables para inicializar los archivos de proyecto de SharePoint con los datos recopilados en el asistente.

  • Crear un nuevo archivo .snk en cada nueva instancia de proyecto de columnas de sitio. Este archivo se usa para firmar el resultado del proyecto a fin de que el ensamblado de la solución de SharePoint se pueda implementar en la memoria caché global de ensamblados.

  • Depurar y probar el asistente.

Nota

Puede descargar un ejemplo que contiene los proyectos completos, el código y otros archivos para este tutorial de la siguiente ubicación: https://go.microsoft.com/fwlink/?LinkId=191369.

Requisitos previos

Para realizar este tutorial, debe crear primero la solución SiteColumnProjectItem con el tutorial Tutorial: Crear un elemento de proyecto de columna de sitio con una plantilla de proyecto, parte 1.

También necesitará los siguientes componentes en el equipo de desarrollo para completar este tutorial:

El conocimiento de los siguientes conceptos es útil, aunque no necesario, para completar el tutorial.

Descripción de los componentes del asistente

El asistente que se muestra en este tutorial contiene varios componentes. En la tabla siguiente se describen estos componentes.

Componente

Descripción

Implementación del asistente

Esta es una clase denominada SiteColumnProjectWizard, que implementa la interfaz IWizard. Esta interfaz define los métodos que Visual Studio llama cuando el asistente se inicia y finaliza y, a veces, mientras se ejecuta.

Interfaz de usuario del asistente

Se trata de una ventana basada en WPF, denominada WizardWindow. Esta ventana incluye dos controles de usuario, denominados Page1 y Page2. Estos controles de usuario representan las dos páginas del asistente.

En este tutorial, el método RunStarted de la implementación del asistente muestra la interfaz de usuario del asistente.

Modelo de datos del asistente

Esta es una clase intermediaria denominada SiteColumnWizardModel, que proporciona una capa entre la interfaz de usuario del asistente y la implementación del asistente. En este ejemplo se usa esta clase para ayudar a abstraer la implementación del asistente y la interfaz de usuario del asistente entre sí; esta clase no es un componente necesario de todos los asistentes.

En este tutorial, la implementación del asistente pasa un objeto SiteColumnWizardModel a la ventana del asistente cuando muestra la interfaz de usuario del asistente. La interfaz de usuario del asistente usa los métodos de este objeto para guardar los valores de los controles de la interfaz de usuario y para realizar tareas como comprobar que la dirección URL del sitio de entrada es válido. Una vez que el usuario finaliza el asistente, la implementación del asistente usa el objeto SiteColumnWizardModel para determinar el estado final de la interfaz de usuario.

Administrador de firma de proyectos

Esta es una clase auxiliar denominada ProjectSigningManager, que se usa en la implementación del asistente para crear un nuevo archivo key.snk en cada nueva instancia de proyecto.

Comandos de SharePoint

Estos son los métodos usados por el modelo de datos del asistente para llamar al sitio de SharePoint local mientras el asistente se está ejecutando. Dado que los comandos de SharePoint deben tener .NET Framework 3.5 como destino, se implementan en un ensamblado diferente al del resto del código del asistente.

Crear los proyectos

Para completar este tutorial, debe agregar varios proyectos a la solución SiteColumnProjectItem que creó en Tutorial: Crear un elemento de proyecto de columna de sitio con una plantilla de proyecto, parte 1.

  • Un proyecto WPF. Implementará la interfaz IWizard y definirá la interfaz de usuario del asistente de este proyecto.

  • Un proyecto de biblioteca de clases que define los comandos de SharePoint. Este proyecto debe tener como destino .NET Framework 3.5.

Comience el tutorial creando ambos proyectos.

Para crear el proyecto WPF

  1. Abra la solución SiteColumnProjectItem en Visual Studio.

  2. En el Explorador de soluciones, haga clic en el nodo de la solución con el botón secundario, seleccione Agregar y haga clic en Nuevo proyecto.

    Nota

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

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

  4. Asegúrese de que .NET Framework 4 esté seleccionado en el cuadro combinado de la parte superior del cuadro de diálogo Agregar nuevo proyecto.

  5. Seleccione la plantilla de proyecto Biblioteca de controles de usuario de WPF.

  6. En el cuadro Nombre, escriba ProjectTemplateWizard.

  7. Haga clic en Aceptar.

    Visual Studio agrega el proyecto ProjectTemplateWizard a la solución.

  8. Elimine el archivo UserControl1.xaml del proyecto.

Para crear el proyecto Comandos de SharePoint

  1. En el Explorador de soluciones, haga clic en el nodo de la solución con el botón secundario, seleccione Agregar y haga clic en Nuevo proyecto.

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

  3. Haga clic en la plantilla de proyecto Biblioteca de clases.

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

  5. En el cuadro Nombre, escriba ComandosSharePoint.

  6. Haga clic en Aceptar.

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

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

Configurar los proyectos

Antes de crear el asistente, tiene que agregar algunos archivos de código y referencias de ensamblado a los proyectos.

Para configurar el proyecto de asistente

  1. En el Explorador de soluciones, haga clic con el botón secundario en el nodo de proyecto ProjectTemplateWizard y, a continuación, haga clic en Propiedades.

  2. En el Diseñador de proyectos, cambie el marco de trabajo de destino de .NET Framework Client Profile 4 a .NET Framework 4. En los proyectos de Visual C#, puede hacerlo en la pestaña Aplicación; en los proyectos de Visual Basic, puede hacerlo en la pestaña Compilar. Para obtener más información, vea Cómo: Elegir perfil o versión de destino de .NET Framework.

    Nota

    De forma predeterminada, al crear un nuevo proyecto destinado a .NET Framework 4, el proyecto es para la versión Client Profile. Este tutorial requiere la versión completa de .NET Framework 4.

  3. En el proyecto ProjectTemplateWizard, agregue un nuevo elemento Window (WPF) al proyecto. Asigne el nombre WizardWindow al elemento.

  4. Agregue dos nuevos elementos User Control (WPF) al proyecto. Asigne a estos elementos los nombres Page1 y Page2.

  5. Agregue cuatro archivos de código con los nombres siguientes:

    • SiteColumnProjectWizard

    • SiteColumnWizardModel

    • ProjectSigningManager

    • CommandIds

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

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

    • EnvDTE

    • Microsoft.VisualStudio.OLE.Interop

    • Microsoft.VisualStudio.SharePoint

    • Microsoft.VisualStudio.Shell.10.0

    • Microsoft.VisualStudio.Shell.Interop.10.0

    • Microsoft.VisualStudio.TemplateWizardInterface

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

    Nota

    En los proyectos de Visual Basic, primero debe hacer clic en el botón Mostrar todos los archivos en el Explorador de soluciones para ver la carpeta Referencias.

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

  10. Si está desarrollando un proyecto de Visual Basic, importe el espacio de nombres ProjectTemplateWizard al proyecto mediante el Diseñador de proyectos. Para obtener más información, vea Cómo: Agregar o quitar espacios de nombres importados (Visual Basic).

Para configurar el proyecto ComandosSharePoint

  1. En el proyecto ComandosSharePoint, agregue un archivo de código llamado Commands.

  2. En el Explorador de soluciones, haga clic con el botón secundario en el nodo del proyecto ComandosSharePoint.

  3. En el menú Proyecto, seleccione Agregar elemento existente.

  4. En el cuadro de diálogo Agregar elemento existente, busque la carpeta que contiene los archivos de código del proyecto ProjectTemplateWizard.

  5. Seleccione el archivo de código CommandIds.

  6. Haga clic en el menú desplegable del botón Agregar y seleccione Agregar como vínculo.

    Visual Studio agrega el archivo de código al proyecto SharePointCommands como vínculo. Esto significa que el archivo de código se encuentra en el proyecto ProjectTemplateWizard, pero el código del archivo también se compila en el proyecto SharePointCommands.

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

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

    • Microsoft.SharePoint

    • Microsoft.VisualStudio.SharePoint.Commands

Crear el modelo de asistente, el administrador de firma y los identificadores de comando de SharePoint

Agregue el código al proyecto de ProjectTemplateWizard para implementar los componentes siguientes en el ejemplo:

  • Los identificadores de comando de SharePoint. Son las cadenas que identifican los comandos de SharePoint usados por el asistente. Más adelante en este tutorial, agregará al proyecto SharePointCommands el código para implementar los comandos.

  • El modelo de datos del asistente.

  • El administrador de firma del proyecto.

Para obtener más información sobre estos componentes, vea Descripción de los componentes del asistente.

Para definir los identificadores de comando de SharePoint

  1. En el proyecto ProjectTemplateWizard, abra el archivo de código CommandIds.

  2. Reemplace todo el contenido de este archivo por el código siguiente:

    Namespace Contoso.SharePoint.Commands
        Public Class CommandIds
            Public Const GetFieldTypes As String = "Contoso.Commands.GetFieldTypes"
            Public Const ValidateSite As String = "Contoso.Commands.ValidateSite"
        End Class
    End Namespace
    
    namespace Contoso.SharePoint.Commands
    {
        public static class CommandIds
        {
            public const string GetFieldTypes = "Contoso.Commands.GetFieldTypes";
            public const string ValidateSite = "Contoso.Commands.ValidateSite";
        }
    }
    

Para crear el modelo de asistente

  1. Abra el archivo de código SiteColumnWizardModel.

  2. Reemplace todo el contenido de este archivo por el código siguiente:

    Imports EnvDTE
    Imports Microsoft.VisualStudio.SharePoint
    Imports Microsoft.VisualStudio
    Imports Microsoft.VisualStudio.Shell
    Imports Microsoft.VisualStudio.Shell.Interop
    Imports IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider
    
    Public Class SiteColumnWizardModel
        Private dteObject As DTE
        Private projectServiceValue As ISharePointProjectService
        Private validatedUrls As New List(Of String)
    
        Friend Sub New(ByVal dteObject As DTE, ByVal requiresFarmPriveleges As Boolean)
            Me.dteObject = dteObject
    
            ' Initialize default values for wizard choices.
            IsSandboxed = Not requiresFarmPriveleges
            IsSecondPagePopulated = False
            FieldType = "Text"
            FieldGroup = "Custom Columns"
            FieldName = "My Custom Column"
            CurrentSiteUrl = GetLocalHostUrl()
        End Sub
    
    #Region "Helper methods used by the wizard UI"
    
        ' Specifies whether the current site URL is valid. Uses the ValidateSite SharePoint command to do this.
        Friend Function ValidateCurrentUrl(ByVal errorMessage As String) As Boolean
            Dim isValid As Boolean = False
            errorMessage = String.Empty
    
            If validatedUrls.Contains(CurrentSiteUrl) Then
                isValid = True
            Else
                Dim uriToValidate As Uri = New Uri(CurrentSiteUrl, UriKind.Absolute)
                Dim vsThreadedWaitDialog As IVsThreadedWaitDialog2 = Nothing
    
                Try
                    vsThreadedWaitDialog = ShowProgressDialog("Connect to SharePoint",
                        "Connecting to SharePoint site " + CurrentSiteUrl)
                    isValid = Me.ProjectService.SharePointConnection.ExecuteCommand(Of Uri, Boolean)(
                        Contoso.SharePoint.Commands.CommandIds.ValidateSite, uriToValidate)
                Catch ex As Exception
                    errorMessage = "An error occurred while validating the site. " + ex.Message
                Finally
                    If isValid Then
                        validatedUrls.Add(CurrentSiteUrl)
                    End If
                    If vsThreadedWaitDialog IsNot Nothing Then
                        CloseProgressDialog(vsThreadedWaitDialog)
                    End If
                End Try
            End If
            Return isValid
        End Function
    
        ' Gets the available field types from the SharePoint site. Uses the GetFieldTypes SharePoint command to do this.
        Friend Function GetFieldTypes() As ArrayList
            ' If we have not yet validated this site, do it now.
            Dim errorMessage As String = String.Empty
            If Not ValidateCurrentUrl(errorMessage) Then
                MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                    CurrentSiteUrl, errorMessage), "SharePoint Connection Error")
                Return Nothing
            End If
    
            ' Site is valid, so go ahead and get the available field types.
            Dim siteUri As Uri = New Uri(CurrentSiteUrl, UriKind.Absolute)
            Dim vsThreadedWaitDialog As IVsThreadedWaitDialog2 = ShowProgressDialog(
                "Connect to SharePoint", "Connecting to SharePoint site " + CurrentSiteUrl)
            Dim fieldTypesArray As String() = Me.ProjectService.SharePointConnection.ExecuteCommand(Of Uri, String())(
                Contoso.SharePoint.Commands.CommandIds.GetFieldTypes, siteUri)
    
            If vsThreadedWaitDialog IsNot Nothing Then
                CloseProgressDialog(vsThreadedWaitDialog)
            End If
    
            Return New ArrayList(fieldTypesArray)
        End Function
    
        ' Returns the default column group names in SharePoint.
        Friend Function GetFieldGroups() As List(Of String)
            Dim groups As List(Of String) = New List(Of String)()
            groups.Add("Base Columns")
            groups.Add("Core Contact and Calendar Columns")
            groups.Add("Core Document Columns")
            groups.Add("Core Task and Issue Columns")
            groups.Add("Extended Columns")
            Return groups
        End Function
    #End Region
    
    #Region "Properties shared by the wizard implementation and the wizard UI"
    
        Friend ReadOnly Property ProjectService As ISharePointProjectService
            Get
                If projectServiceValue Is Nothing Then
                    projectServiceValue = GetProjectService()
                End If
                Return projectServiceValue
            End Get
        End Property
    
        Friend Property IsSecondPagePopulated As Boolean
        Friend Property IsSandboxed As Boolean
        Friend Property FieldType As String
        Friend Property FieldGroup As String
        Friend Property FieldName As String
        Friend Property CurrentSiteUrl As String
    #End Region
    
    #Region "Private methods"
    
        Private Function GetLocalHostUrl() As String
            Const HttpScheme As String = "http"
            Dim builder As UriBuilder = New UriBuilder(HttpScheme, Environment.MachineName.ToLowerInvariant())
            Return builder.ToString()
        End Function
    
        Private Function GetProjectService() As ISharePointProjectService
            Dim serviceProvider As ServiceProvider = New ServiceProvider(CType(dteObject, IOleServiceProvider))
            Return CType(serviceProvider.GetService(GetType(ISharePointProjectService)), ISharePointProjectService)
        End Function
    
        Private Function ShowProgressDialog(ByVal caption As String, ByVal message As String) As IVsThreadedWaitDialog2
            Dim oleServiceProvider As IOleServiceProvider = CType(dteObject, IOleServiceProvider)
            Dim dialogFactory As IVsThreadedWaitDialogFactory = CType(New ServiceProvider(oleServiceProvider).GetService(
                GetType(SVsThreadedWaitDialogFactory)), IVsThreadedWaitDialogFactory)
    
            If dialogFactory Is Nothing Then
                Throw New InvalidOperationException("The IVsThreadedWaitDialogFactory object could not be retrieved.")
            End If
    
            Dim vsThreadedWaitDialog As IVsThreadedWaitDialog2 = Nothing
            ErrorHandler.ThrowOnFailure(dialogFactory.CreateInstance(vsThreadedWaitDialog))
            ErrorHandler.ThrowOnFailure(vsThreadedWaitDialog.StartWaitDialog(caption, message,
                Nothing, Nothing, String.Empty, 0, False, True))
            Return vsThreadedWaitDialog
        End Function
    
        Private Sub CloseProgressDialog(ByVal vsThreadedWaitDialog As IVsThreadedWaitDialog2)
            If vsThreadedWaitDialog Is Nothing Then
                Throw New ArgumentNullException("vsThreadedWaitDialog")
            End If
            Dim canceled As Integer
            ErrorHandler.ThrowOnFailure(vsThreadedWaitDialog.EndWaitDialog(canceled))
        End Sub
    #End Region
    End Class
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Windows;
    using EnvDTE;
    using Microsoft.VisualStudio.SharePoint;
    using Microsoft.VisualStudio;
    using Microsoft.VisualStudio.Shell;
    using Microsoft.VisualStudio.Shell.Interop;
    using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;
    
    namespace ProjectTemplateWizard
    {
        internal class SiteColumnWizardModel
        {
            private DTE dteObject;
            private ISharePointProjectService projectServiceValue;
            private List<string> validatedUrls = new List<string>();
    
            internal SiteColumnWizardModel(DTE dteObject, bool requiresFarmPriveleges)
            {
                this.dteObject = dteObject;
    
                // Initialize default values for wizard choices.
                IsSandboxed = !requiresFarmPriveleges;
                IsSecondPagePopulated = false;
                FieldType = "Text";
                FieldGroup = "Custom Columns";
                FieldName = "My Custom Column";
                CurrentSiteUrl = GetLocalHostUrl();
            }
    
            #region Helper methods used by the wizard UI
    
            // Specifies whether the current site URL is valid. Uses the ValidateSite SharePoint command to do this.
            internal bool ValidateCurrentUrl(out string errorMessage)
            {
                bool isValid = false;
                errorMessage = String.Empty;
    
                if (validatedUrls.Contains(CurrentSiteUrl))
                {
                    isValid = true;
                }
                else
                {
                    Uri uriToValidate = new Uri(CurrentSiteUrl, UriKind.Absolute);
                    IVsThreadedWaitDialog2 vsThreadedWaitDialog = null;
    
                    try
                    {
                        vsThreadedWaitDialog = ShowProgressDialog("Connect to SharePoint",
                            "Connecting to SharePoint site " + CurrentSiteUrl);
                        isValid = this.ProjectService.SharePointConnection.ExecuteCommand<Uri, bool>(
                            Contoso.SharePoint.Commands.CommandIds.ValidateSite, uriToValidate);
                    }
                    catch (Exception ex)
                    {
                        errorMessage = "An error occurred while validating the site. " + ex.Message;
                    }
                    finally
                    {
                        if (isValid)
                        {
                            validatedUrls.Add(CurrentSiteUrl);
                        }
    
                        if (vsThreadedWaitDialog != null)
                        {
                            CloseProgressDialog(vsThreadedWaitDialog);
                        }
                    }
                }
    
                return isValid;
            }
    
            // Gets the available field types from the SharePoint site. Uses the GetFieldTypes SharePoint command to do this.
            internal ArrayList GetFieldTypes()
            {
                // If we have not yet validated this site, do it now.
                string errorMessage;
                if (!ValidateCurrentUrl(out errorMessage))
                {
                    MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                        CurrentSiteUrl, errorMessage), "SharePoint Connection Error");
                    return null;
                }
    
                // Site is valid, so go ahead and get the available field types.
                Uri siteUri = new Uri(CurrentSiteUrl, UriKind.Absolute);
                IVsThreadedWaitDialog2 vsThreadedWaitDialog = ShowProgressDialog(
                    "Connect to SharePoint", "Connecting to SharePoint site " + CurrentSiteUrl);
                string[] fieldTypesArray = this.ProjectService.SharePointConnection.ExecuteCommand<Uri, string[]>(
                        Contoso.SharePoint.Commands.CommandIds.GetFieldTypes, siteUri);
    
                if (vsThreadedWaitDialog != null)
                {
                    CloseProgressDialog(vsThreadedWaitDialog);
                }
    
                return new ArrayList(fieldTypesArray);
            }
    
            // Returns the default column group names in SharePoint.
            internal List<string> GetFieldGroups()
            {
                List<string> groups = new List<string>();
                groups.Add("Base Columns");
                groups.Add("Core Contact and Calendar Columns");
                groups.Add("Core Document Columns");
                groups.Add("Core Task and Issue Columns");
                groups.Add("Extended Columns");
                return groups;
            }
    
            #endregion
    
            #region Properties shared by the wizard implementation and the wizard UI
    
            internal ISharePointProjectService ProjectService
            {
                get
                {
                    if (projectServiceValue == null)
                    {
                        projectServiceValue = GetProjectService();
                    }
                    return projectServiceValue;
                }
            }
    
            internal bool IsSecondPagePopulated { get; set; }
            internal bool IsSandboxed { get; set; }
            internal string FieldType { get; set; }
            internal string FieldGroup { get; set; }
            internal string FieldName { get; set; }
            internal string CurrentSiteUrl { get; set; }
    
            #endregion
    
            #region Private methods
    
            private string GetLocalHostUrl()
            {
                const string HttpScheme = "http";
                UriBuilder builder = new UriBuilder(HttpScheme, Environment.MachineName.ToLowerInvariant());
                return builder.ToString();
            }
    
            private ISharePointProjectService GetProjectService()
            {
                ServiceProvider serviceProvider = new ServiceProvider(dteObject as IOleServiceProvider);
                return serviceProvider.GetService(typeof(ISharePointProjectService)) as ISharePointProjectService;
            }
    
            private IVsThreadedWaitDialog2 ShowProgressDialog(string caption, string message)
            {
                IOleServiceProvider oleServiceProvider = dteObject as IOleServiceProvider;
                IVsThreadedWaitDialogFactory dialogFactory = new ServiceProvider(oleServiceProvider).GetService(
                    typeof(SVsThreadedWaitDialogFactory)) as IVsThreadedWaitDialogFactory;
    
                if (dialogFactory == null)
                {
                    throw new InvalidOperationException("The IVsThreadedWaitDialogFactory object could not be retrieved.");
                }
    
                IVsThreadedWaitDialog2 vsThreadedWaitDialog = null;
                ErrorHandler.ThrowOnFailure(dialogFactory.CreateInstance(out vsThreadedWaitDialog));
                ErrorHandler.ThrowOnFailure(vsThreadedWaitDialog.StartWaitDialog(caption, message,
                     null, null, String.Empty, 0, false, true));
                return vsThreadedWaitDialog;
            }
    
            private void CloseProgressDialog(IVsThreadedWaitDialog2 vsThreadedWaitDialog)
            {
                if (vsThreadedWaitDialog == null)
                {
                    throw new ArgumentNullException("vsThreadedWaitDialog");
                }
    
                int canceled;
                ErrorHandler.ThrowOnFailure(vsThreadedWaitDialog.EndWaitDialog(out canceled));
            }
    
            #endregion
        }
    }
    

Para crear el administrador de firma de proyecto

  1. Abra el archivo de código ProjectSigningManager.

  2. Reemplace todo el contenido de este archivo por el código siguiente:

    Imports EnvDTE
    Imports System
    Imports System.IO
    Imports System.Runtime.InteropServices
    
    Friend Class ProjectSigningManager
        Private Const KEY_FILENAME As String = "key.snk"
        Private keyBuffer As Byte()
        Private strongNameGenerated As Boolean = False
    
    #Region "Methods used by the project wizard"
    
        Friend Sub GenerateKeyFile()
            If Not strongNameGenerated Then
                keyBuffer = CreateNewKeyPair()
                strongNameGenerated = True
            End If
        End Sub
    
        Friend Sub AddKeyFile(ByVal project As Project)
            If strongNameGenerated Then
                AddKeyFileToProject(project)
            End If
        End Sub
    #End Region
    
    #Region "Private members"
    
        Private Function CreateNewKeyPair() As Byte()
            Dim buffer As IntPtr = IntPtr.Zero
            Dim bufferSize As UInteger
            Dim keyBuffer As Byte()
    
            Try
                If 0 = NativeMethods.StrongNameKeyGen(IntPtr.Zero, 0, buffer, bufferSize) Then
                    Marshal.ThrowExceptionForHR(NativeMethods.StrongNameErrorInfo())
                End If
                If buffer = IntPtr.Zero Then
                    Throw New InvalidOperationException("Cannot generate the strong name key.")
                End If
    
                ' Copy generated key to managed memory.
                keyBuffer = New Byte(bufferSize) {}
                Marshal.Copy(buffer, keyBuffer, 0, CInt(bufferSize))
            Finally
                ' Free native resources.
                NativeMethods.StrongNameFreeBuffer(buffer)
            End Try
            Return keyBuffer
        End Function
    
        Private Sub AddKeyFileToProject(ByVal project As Project)
    
            ' Save the key to a file.
            If keyBuffer IsNot Nothing Then
                Try
                    Dim destinationDirectory As String = Path.GetDirectoryName(project.FullName)
                    Dim keySavePath As String = Path.Combine(destinationDirectory, KEY_FILENAME)
    
                    File.WriteAllBytes(keySavePath, keyBuffer)
                    project.ProjectItems.AddFromFile(keySavePath)
    
                    ' Add properties in the project to use the key for signing.
                    Dim projProps As EnvDTE.Properties = project.Properties
                    projProps.Item("SignAssembly").Value = True
                    projProps.Item("AssemblyOriginatorKeyFile").Value = KEY_FILENAME
                Catch e As Exception
                    Throw New Exception("Cannot add the strong name key to the project. " & e.Message, e)
                End Try
            End If
        End Sub
    
        Private Class NativeMethods
            <DllImport("mscoree.dll")>
            Friend Shared Function StrongNameFreeBuffer(ByVal pbMemory As IntPtr) As Integer
            End Function
    
            <DllImport("mscoree.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True)>
            Friend Shared Function StrongNameKeyGen(ByVal wszKeyContainer As IntPtr, ByVal dwFlags As UInteger, _
                ByRef KeyBlob As IntPtr, ByRef KeyBlobSize As UInteger) As Integer
            End Function
    
            <DllImport("mscoree.dll", CharSet:=CharSet.Unicode)>
            Friend Shared Function StrongNameErrorInfo() As Integer
            End Function
        End Class
    #End Region
    End Class
    
    using EnvDTE;
    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    
    namespace ProjectTemplateWizard
    {
        internal class ProjectSigningManager
        {
            private const string KEY_FILENAME = "key.snk";
            private byte[] keyBuffer;
            private bool strongNameGenerated = false;
    
            #region Methods used by the project wizard
    
            internal void GenerateKeyFile()
            {
                if (!strongNameGenerated)
                {
                    keyBuffer = CreateNewKeyPair();
                    strongNameGenerated = true;
                }
            }
    
            internal void AddKeyFile(Project project)
            {
                if (strongNameGenerated)
                {
                    AddKeyFileToProject(project);
                }
            }
    
            #endregion
    
            #region Private members
    
            private byte[] CreateNewKeyPair()
            {
                IntPtr buffer = IntPtr.Zero;
                uint bufferSize;
                byte[] keyBuffer;
    
                try
                {
                    if (0 == NativeMethods.StrongNameKeyGen(IntPtr.Zero, 0, out buffer, out bufferSize))
                    {
                        Marshal.ThrowExceptionForHR(NativeMethods.StrongNameErrorInfo());
                    }
    
                    if (buffer == IntPtr.Zero)
                    {
                        throw new InvalidOperationException("Cannot generate the strong name key.");
                    }
    
                    // Copy generated key to managed memory.
                    keyBuffer = new byte[bufferSize];
                    Marshal.Copy(buffer, keyBuffer, 0, (int)bufferSize);
                }
                finally
                {
                    // Free native resources.
                    NativeMethods.StrongNameFreeBuffer(buffer);
                }
    
                return keyBuffer;
            }
    
            private void AddKeyFileToProject(Project project)
            {
                // Save the key to a file.
                if (keyBuffer != null)
                {
                    try
                    {
                        string destinationDirectory = Path.GetDirectoryName(project.FullName);
                        string keySavePath = Path.Combine(destinationDirectory, KEY_FILENAME);
    
                        File.WriteAllBytes(keySavePath, keyBuffer);
                        project.ProjectItems.AddFromFile(keySavePath);
    
                        // Add properties in the project to use the key for signing.
                        EnvDTE.Properties projProps = project.Properties;
                        projProps.Item("SignAssembly").Value = true;
                        projProps.Item("AssemblyOriginatorKeyFile").Value = KEY_FILENAME;
                    }
                    catch (Exception e)
                    {
                        throw new Exception("Cannot add the strong name key to the project. " + e.Message, e);
                    }
                }
            }
    
            private static class NativeMethods
            {
                [DllImport("mscoree.dll")]
                internal extern static int StrongNameFreeBuffer(IntPtr pbMemory);
    
                [DllImport("mscoree.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
                internal static extern int StrongNameKeyGen(IntPtr wszKeyContainer, uint dwFlags, out IntPtr KeyBlob, 
                    out uint KeyBlobSize);
    
                [DllImport("mscoree.dll", CharSet = CharSet.Unicode)]
                internal static extern int StrongNameErrorInfo();
            }
    
            #endregion
        }
    }
    

Crear la interfaz de usuario del asistente

Agregue XAML para definir la interfaz de usuario de la ventana del asistente y los dos controles de usuario que proporcionan la interfaz de usuario para las páginas del asistente, y agregue el código para definir el comportamiento de la ventana y los controles de usuario. El asistente que crea se parece al asistente integrado para los proyectos de SharePoint en Visual Studio 2010.

Nota

En los siguientes pasos, el proyecto tendrá algunos errores de compilación después de agregar XAML o el código al proyecto. Estos errores desaparecerán al agregar código en pasos posteriores.

Para crear la interfaz de usuario de la ventana del asistente

  1. En el proyecto ProjectTemplateWizard, haga doble clic en el archivo WizardWindow.xaml para abrir la ventana en el diseñador.

  2. En la vista XAML del diseñador, reemplace el código XAML actual por el siguiente. El XAML define una interfaz de usuario que incluye un título, un control Grid que contiene las páginas del asistente y los botones de navegación en la parte inferior de la ventana.

    <ui:DialogWindow x:Class="ProjectTemplateWizard.WizardWindow"
                     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
                     xmlns:ui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.10.0"        
                     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"                                
                     Title="SharePoint Customization Wizard" Height="500" Width="700" ResizeMode="NoResize" 
                     Loaded="Window_Loaded" TextOptions.TextFormattingMode="Display">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="75*" />
                <RowDefinition Height="364*" />
                <RowDefinition Height="1*" />
                <RowDefinition Height="60*" />
            </Grid.RowDefinitions>
            <Grid Grid.Row="0" Name="headingGrid" Background="White">
                <Label Height="28" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="18,0,0,0" 
                       Name="headingLabel" FontWeight="ExtraBold" />
            </Grid>
            <Grid Grid.Row="1" Name="pageGrid" />
            <Rectangle Grid.Row="2" Name="separatorRectangle" Fill="White"  />
            <StackPanel Grid.Row="3" Name="navigationPanel" Orientation="Horizontal">
                <Button Content="&lt; _Previous" Margin="300,0,0,0"  Height="25" Name="previousButton" Width="85" 
                        IsEnabled="False" Click="previousButton_Click" />
                <Button Content="_Next >" Margin="10,0,0,0" Height="25" Name="nextButton" Width="85" Click="nextButton_Click" 
                        IsDefault="True" />
                <Button Content="_Finish" Margin="10,0,0,0" Height="25" Name="finishButton" Width="85" 
                        Click="finishButton_Click" />
                <Button Content="Cancel" Margin="10,0,0,0" Height="25" Name="cancelButton" Width="85" 
                        IsCancel="True" />
            </StackPanel>
        </Grid>
    </ui:DialogWindow>
    

    Nota

    La ventana que se crea en este XAML se deriva de la clase base DialogWindow. Cuando se agrega un cuadro de diálogo WPF personalizado en Visual Studio, se recomienda derivar el cuadro de diálogo de esta clase para que tenga un estilo coherente con otros cuadros de diálogo de Visual Studio y para evitar los problemas de diálogo modal que pueden producirse en caso contrario. Para obtener más información, vea How to: Create and Manage Dialog Boxes.

  3. Si está desarrollando un proyecto de Visual Basic, quite el espacio de nombres ProjectTemplateWizard del nombre de clase WizardWindow del atributo x:Class del elemento Window. Esta es la primera línea del código XAML. Cuando termine, la primera línea tendrá el aspecto siguiente:

    <Window x:Class="WizardWindow"
    
  4. Abra el archivo de código subyacente del archivo WizardWindow.xaml.

  5. Reemplace todo el contenido de este archivo por el código siguiente:

    Public Class WizardWindow
        Private firstPage As Page1
        Private secondPage As Page2
        Private Const firstPageLabel As String = "Specify the site and security level for debugging"
        Private Const secondPageLabel As String = "Configure the site column"
    
        Friend Sub New(ByVal presentationModel As SiteColumnWizardModel)
            InitializeComponent()
            Me.PresentationModel = presentationModel
            firstPage = New Page1(Me)
            secondPage = New Page2(Me)
            secondPage.Visibility = Visibility.Hidden
        End Sub
    
        Friend Property PresentationModel As SiteColumnWizardModel
    
        Private Sub Window_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            headingLabel.Content = firstPageLabel
            pageGrid.Children.Add(firstPage)
            pageGrid.Children.Add(secondPage)
        End Sub
    
        Private Sub nextButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    
            ' Initialize the second wizard page if this is the first time 
            ' it has been shown with the current site URL.
            If Not PresentationModel.IsSecondPagePopulated Then
    
                If Not ValidateUrl() Then
                    Return
                End If
    
                ' Refresh the UI in the second page.
                secondPage.ClearControls()
                secondPage.PopulateSiteColumnOptions()
    
                ' Do not do this work again until the user changes the site URL.
                PresentationModel.IsSecondPagePopulated = True
            End If
    
            ' Display the second wizard page and update related controls.
            firstPage.Visibility = Visibility.Hidden
            secondPage.Visibility = Visibility.Visible
            previousButton.IsEnabled = True
            nextButton.IsEnabled = False
            nextButton.IsDefault = False
            finishButton.IsDefault = True
            headingLabel.Content = secondPageLabel
        End Sub
    
        ' Display the first wizard page again and update related controls.
        Private Sub previousButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            secondPage.Visibility = Visibility.Hidden
            firstPage.Visibility = Visibility.Visible
            previousButton.IsEnabled = False
            finishButton.IsDefault = False
            nextButton.IsEnabled = True
            nextButton.IsDefault = True
            headingLabel.Content = firstPageLabel
        End Sub
    
        Private Sub finishButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            If ValidateUrl() Then
                DialogResult = True
                Close()
            End If
        End Sub
    
        Private Function ValidateUrl() As Boolean
            Dim errorMessage As String = String.Empty
            If Not PresentationModel.ValidateCurrentUrl(errorMessage) Then
                MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                    PresentationModel.CurrentSiteUrl, errorMessage),
                    "SharePoint Connection Error")
                Return False
            End If
            Return True
        End Function
    End Class
    
    using System;
    using System.Windows;
    using Microsoft.VisualStudio.PlatformUI;
    
    namespace ProjectTemplateWizard
    {
        public partial class WizardWindow : DialogWindow
        {
            private Page1 firstPage;
            private Page2 secondPage;
            private const string firstPageLabel = "Specify the site and security level for debugging";
            private const string secondPageLabel = "Configure the site column";
    
            internal WizardWindow(SiteColumnWizardModel presentationModel)
            {
                InitializeComponent();
                this.PresentationModel = presentationModel;
                firstPage = new Page1(this);
                secondPage = new Page2(this);
                secondPage.Visibility = Visibility.Hidden;
            }
    
            internal SiteColumnWizardModel PresentationModel { get; set; }
    
            private void Window_Loaded(object sender, RoutedEventArgs e)
            {
                headingLabel.Content = firstPageLabel;
                pageGrid.Children.Add(firstPage);
                pageGrid.Children.Add(secondPage);
            }
    
            private void nextButton_Click(object sender, RoutedEventArgs e)
            {
                // Initialize the second wizard page if this is the first time 
                // it has been shown with the current site URL.
                if (!PresentationModel.IsSecondPagePopulated)
                {
                    if (!ValidateUrl())
                    {
                        return;
                    }
    
                    // Refresh the UI in the second page.
                    secondPage.ClearControls();
                    secondPage.PopulateSiteColumnOptions();
    
                    // Do not do this work again until the user changes the site URL.
                    PresentationModel.IsSecondPagePopulated = true;
                }
    
                // Display the second wizard page and update related controls.
                firstPage.Visibility = Visibility.Hidden;
                secondPage.Visibility = Visibility.Visible;
                previousButton.IsEnabled = true;
                nextButton.IsEnabled = false;
                finishButton.IsDefault = true;
                headingLabel.Content = secondPageLabel;
            }
    
            // Display the first wizard page again and update related controls.
            private void previousButton_Click(object sender, RoutedEventArgs e)
            {
                secondPage.Visibility = Visibility.Hidden;
                firstPage.Visibility = Visibility.Visible;
                previousButton.IsEnabled = false;
                finishButton.IsDefault = false;
                nextButton.IsEnabled = true;
                nextButton.IsDefault = true;
                headingLabel.Content = firstPageLabel;
            }
    
            private void finishButton_Click(object sender, RoutedEventArgs e)
            {
                if (ValidateUrl())
                {
                    DialogResult = true;
                    Close();
                }
            }
    
            private bool ValidateUrl()
            {
                string errorMessage;
                if (!PresentationModel.ValidateCurrentUrl(out errorMessage))
                {
                    MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                        PresentationModel.CurrentSiteUrl, errorMessage),
                        "SharePoint Connection Error");
                    return false;
                }
                return true;
            }
        }
    }
    

Para crear la interfaz de usuario de la primera página del asistente

  1. Haga doble clic en el archivo Page1.xaml para abrir el control de usuario en el diseñador.

  2. En la vista XAML del diseñador, reemplace el código XAML actual por el siguiente. El XAML define una interfaz de usuario que incluye un cuadro de texto donde los usuarios pueden escribir la dirección URL del sitio local que desean usar para depurar y los botones de radio para especificar si el proyecto está en un espacio aislado.

    <UserControl x:Class="ProjectTemplateWizard.Page1"
                 xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="https://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" d:DesignHeight="364" d:DesignWidth="700" Loaded="UserControl_Loaded">
        <Grid Height="364" HorizontalAlignment="Left" Name="page1Grid" Width="700">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20*" />
                <ColumnDefinition Width="548*" />
                <ColumnDefinition Width="132*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <TextBox Grid.Row="1" Grid.Column="1" Margin="5,0,1,0" Height="23" Name="siteUrlTextBox" 
                     TextChanged="siteUrlTextBox_TextChanged" />
            <Label Grid.Row="0" Grid.Column="1" Margin="0,20,0,0" Name="siteLabel" FontWeight="Bold" 
                   Target="{Binding ElementName=siteUrlTextBox}" 
                   Content="What local _site do you want to use for debugging?" />
            <Button Grid.Row="1" Grid.Column="2" Content="_Validate" Height="25" Name="validateButton" 
                    Width="88" Click="validateButton_Click" HorizontalAlignment="Left" 
                    Margin="5,0,0,0" VerticalAlignment="Top" />
            <Label Grid.Row="2" Grid.Column="1" Margin="0,10,0,0" Content="What is the trust level for this SharePoint solution?" 
                   Name="trustLabel" FontWeight="Bold" />
            <StackPanel Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="2" Orientation="Vertical">
                <RadioButton Content="Deploy as a sand_boxed solution" Margin="5,0,0,0" Name="sandboxedSolutionRadioButton" 
                             FontWeight="Bold" Checked="sandboxedSolutionRadioButton_Checked" />
                <TextBlock TextWrapping="WrapWithOverflow" Margin="20,7,50,0">Clicking this option causes the solution to be 
                           deployed as a Sandboxed solution. Sandboxed solutions can be deployed by the site collection owner 
                           and are run in a secure, monitored process that has limited resource access.</TextBlock>
                <RadioButton Content="Deploy as a _farm solution" Margin="5,7,0,0" Name="farmSolutionRadioButton" FontWeight="Bold" 
                             Checked="farmSolutionRadioButton_Checked" />
                <TextBlock TextWrapping="WrapWithOverflow" Margin="20,7,50,0">Clicking this option means that users must have 
                           SharePoint administrator privileges to run or deploy the solution.</TextBlock>
            </StackPanel>
        </Grid>
    </UserControl>
    
  3. Si está desarrollando un proyecto de Visual Basic, quite el espacio de nombres ProjectTemplateWizard del nombre de clase Page1 del atributo x:Class del elemento UserControl. Esta es la primera línea del código XAML. Cuando termine, la primera línea tendrá el aspecto siguiente:

    <UserControl x:Class="Page1"
    
  4. Abra el archivo de código subyacente del archivo Page1.xaml.

  5. Reemplace todo el contenido de este archivo por el código siguiente:

    Public Class Page1
        Private mainWindow As WizardWindow
    
        Friend Sub New(ByVal mainWindow As WizardWindow)
            Me.mainWindow = mainWindow
            InitializeComponent()
        End Sub
    
        Private Sub UserControl_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            If (mainWindow.PresentationModel.IsSandboxed) Then
                sandboxedSolutionRadioButton.IsChecked = True
            Else
                sandboxedSolutionRadioButton.IsEnabled = False
                farmSolutionRadioButton.IsChecked = True
            End If
            siteUrlTextBox.Text = mainWindow.PresentationModel.CurrentSiteUrl
        End Sub
    
        ' Validate that the URL exists on the development computer.
        Private Sub validateButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
            Dim errorMessage As String = String.Empty
            validateButton.IsEnabled = False
    
            If Not mainWindow.PresentationModel.ValidateCurrentUrl(errorMessage) Then
                MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                    mainWindow.PresentationModel.CurrentSiteUrl, errorMessage),
                    "SharePoint Connection Error")
            Else
                MessageBox.Show("Successfully connected to SharePoint site " +
                    mainWindow.PresentationModel.CurrentSiteUrl, "Connection Successful")
            End If
            validateButton.IsEnabled = True
        End Sub
    
        ' Prevent users from finishing the wizard if the URL is not formatted correctly.
        Private Sub siteUrlTextBox_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
            Dim url As String = EnsureTrailingSlash(siteUrlTextBox.Text)
    
            ' Perform some basic error-checking on the URL here.
            If url.Length > 0 AndAlso Uri.IsWellFormedUriString(Uri.EscapeUriString(url), UriKind.Absolute) Then
    
                mainWindow.finishButton.IsEnabled = True
                mainWindow.nextButton.IsEnabled = True
                validateButton.IsEnabled = True
                mainWindow.PresentationModel.CurrentSiteUrl = url
                mainWindow.PresentationModel.IsSecondPagePopulated = False
            Else
                mainWindow.finishButton.IsEnabled = False
                mainWindow.nextButton.IsEnabled = False
                validateButton.IsEnabled = False
            End If
        End Sub
    
        Private Sub sandboxedSolutionRadioButton_Checked(ByVal sender As Object, ByVal e As RoutedEventArgs)
            mainWindow.PresentationModel.IsSandboxed = CBool(sandboxedSolutionRadioButton.IsChecked)
        End Sub
    
        Private Sub farmSolutionRadioButton_Checked(ByVal sender As Object, ByVal e As RoutedEventArgs)
            mainWindow.PresentationModel.IsSandboxed = CBool(sandboxedSolutionRadioButton.IsChecked)
        End Sub
    
        Private Function EnsureTrailingSlash(ByVal url As String)
            If Not String.IsNullOrEmpty(url) AndAlso url(url.Length - 1) <> "/" Then
                url += "/"
            End If
            Return url
        End Function
    End Class
    
    using System;
    using System.Windows;
    using System.Windows.Controls;
    
    namespace ProjectTemplateWizard
    {
        public partial class Page1 : UserControl
        {
            private WizardWindow mainWindow;
    
            internal Page1(WizardWindow mainWindow)
            {
                this.mainWindow = mainWindow;
                InitializeComponent();
            }
    
            private void UserControl_Loaded(object sender, RoutedEventArgs e)
            {
                if (mainWindow.PresentationModel.IsSandboxed)
                {
                    sandboxedSolutionRadioButton.IsChecked = true;
                }
                else
                {
                    sandboxedSolutionRadioButton.IsEnabled = false;
                    farmSolutionRadioButton.IsChecked = true;
                }
    
                siteUrlTextBox.Text = mainWindow.PresentationModel.CurrentSiteUrl;
            }
    
            // Validate that the URL exists on the development computer.
            private void validateButton_Click(object sender, RoutedEventArgs e)
            {
                string errorMessage;
                validateButton.IsEnabled = false;
    
                if (!mainWindow.PresentationModel.ValidateCurrentUrl(out errorMessage))
                {
                    MessageBox.Show(String.Format("Cannot connect to the SharePoint site: {0}. {1}",
                        mainWindow.PresentationModel.CurrentSiteUrl, errorMessage),
                        "SharePoint Connection Error");
                }
                else
                {
                    MessageBox.Show("Successfully connected to SharePoint site " +
                        mainWindow.PresentationModel.CurrentSiteUrl, "Connection Successful");
                }
    
                validateButton.IsEnabled = true;
            }
    
            // Prevent users from finishing the wizard if the URL is not formatted correctly.
            private void siteUrlTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                string url = EnsureTrailingSlash(siteUrlTextBox.Text);
    
                // Perform some basic error-checking on the URL here.
                if ((url.Length > 0) && (Uri.IsWellFormedUriString(Uri.EscapeUriString(url), UriKind.Absolute)))
                {
                    mainWindow.finishButton.IsEnabled = true;
                    mainWindow.nextButton.IsEnabled = true;
                    validateButton.IsEnabled = true;
                    mainWindow.PresentationModel.CurrentSiteUrl = url;
                    mainWindow.PresentationModel.IsSecondPagePopulated = false;
                }
                else
                {
                    mainWindow.finishButton.IsEnabled = false;
                    mainWindow.nextButton.IsEnabled = false;
                    validateButton.IsEnabled = false;
                }
            }
    
            private void sandboxedSolutionRadioButton_Checked(object sender, RoutedEventArgs e)
            {
                mainWindow.PresentationModel.IsSandboxed = (bool)sandboxedSolutionRadioButton.IsChecked;
            }
    
            private void farmSolutionRadioButton_Checked(object sender, RoutedEventArgs e)
            {
                mainWindow.PresentationModel.IsSandboxed = (bool)sandboxedSolutionRadioButton.IsChecked;
            }
    
            private string EnsureTrailingSlash(string url)
            {
                if (!String.IsNullOrEmpty(url)
                    && url[url.Length - 1] != '/')
                {
                    url += '/';
                }
                return url;
            }
        }
    }
    

Para crear la interfaz de usuario de la segunda página del asistente

  1. Haga doble clic en el archivo Page2.xaml para abrir el control de usuario en el diseñador.

  2. En la vista XAML del diseñador, reemplace el código XAML actual por el siguiente. El XAML define una interfaz de usuario que incluye una lista desplegable para elegir el tipo base de la columna de sitio, un cuadro combinado para especificar un grupo integrado o personalizado en los que se muestra la columna de sitio en la galería y un cuadro de texto para especificar el nombre de la columna de sitio.

    <UserControl x:Class="ProjectTemplateWizard.Page2"
                 xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="https://schemas.microsoft.com/expression/blend/2008" 
                 mc:Ignorable="d" d:DesignHeight="364" d:DesignWidth="700" Loaded="UserControl_Loaded">
        <Grid Height="364" HorizontalAlignment="Left" Name="page2Grid" Width="700">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="20*" />
                <ColumnDefinition Width="Auto" />
                <ColumnDefinition Width="450*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <Label Grid.Row="0" Grid.Column="1" Margin="0,20,0,0" Content="_Type:" Name="fieldTypeLabel" 
                   FontWeight="Bold" Target="{Binding ElementName=fieldTypeComboBox}"/>
            <Label Grid.Row="1" Grid.Column="1" Margin="0,10,0,0" Content="_Group:" Name="groupLabel" 
                   FontWeight="Bold" Target="{Binding ElementName=groupComboBox}"/>
            <Label Grid.Row="2" Grid.Column="1" Margin="0,10,0,0" Content="_Name:" Name="nameLabel" 
                   FontWeight="Bold" Target="{Binding ElementName=nameTextBox}"/>
            <ComboBox Grid.Row="0" Grid.Column="2" HorizontalAlignment="Left" Margin="0,20,0,0" Height="23" 
                      Name="fieldTypeComboBox" Width="450" SelectionChanged="fieldTypeComboBox_SelectionChanged" />
            <ComboBox Grid.Row="1" Grid.Column="2" HorizontalAlignment="Left" Margin="0,10,0,0" Height="23" 
                      Name="groupComboBox" Width="450" IsEditable="True"  />
            <TextBox Grid.Row="2" Grid.Column="2" HorizontalAlignment="Left"  Margin="0,10,0,0" Height="23" 
                     Name="nameTextBox" Width="450" TextChanged="nameTextBox_TextChanged" />
        </Grid>
    </UserControl>
    
  3. Si está desarrollando un proyecto de Visual Basic, quite el espacio de nombres ProjectTemplateWizard del nombre de clase Page2 del atributo x:Class del elemento UserControl. Esta es la primera línea del código XAML. Cuando termine, la primera línea tendrá el aspecto siguiente:

    <UserControl x:Class="Page2"
    
  4. Abra el archivo de código subyacente del archivo Page2.xaml.

  5. Reemplace todo el contenido de este archivo por el código siguiente:

    Public Class Page2
        Private mainWindow As WizardWindow
        Private innerTextBoxForGroupComboBox As TextBox
    
        Friend Sub New(ByVal mainWindow As WizardWindow)
            Me.mainWindow = mainWindow
            InitializeComponent()
        End Sub
    
        Friend Sub ClearControls()
            fieldTypeComboBox.Items.Clear()
            groupComboBox.Items.Clear()
            nameTextBox.Clear()
        End Sub
    
        Friend Sub PopulateSiteColumnOptions()
            ' Add the available field type names to the combo box.
            Dim fieldTypes As System.Collections.ArrayList = mainWindow.PresentationModel.GetFieldTypes()
            If fieldTypes IsNot Nothing Then
                fieldTypes.Sort()
                For Each fieldValue As String In fieldTypes
                    fieldTypeComboBox.Items.Add(fieldValue)
                Next
                fieldTypeComboBox.SelectedIndex = 0
            End If
    
            ' Add the default group names to the combo box.
            Dim fieldGroups As List(Of String) = mainWindow.PresentationModel.GetFieldGroups()
            For Each fieldGroupValue As String In fieldGroups
                groupComboBox.Items.Add(fieldGroupValue)
            Next
            groupComboBox.SelectedIndex = 0
        End Sub
    
        Private Sub UserControl_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
            ' Handle the TextChanged event of the underlying TextBox for the ComboBox. This enables us to determine 
            ' 1) when the user selects an item in the list and 2) when they type their own custom group name. 
            ' The ComboBox.SelectionChanged event is not raised when you type in an editable ComboboBox.
            innerTextBoxForGroupComboBox = CType(groupComboBox.Template.FindName(
                "PART_EditableTextBox", groupComboBox), TextBox)
            AddHandler innerTextBoxForGroupComboBox.TextChanged, AddressOf innerTextBoxForGroupComboBox_TextChanged
        End Sub
    
        Private Sub fieldTypeComboBox_SelectionChanged(ByVal sender As Object, ByVal e As SelectionChangedEventArgs)
            mainWindow.PresentationModel.FieldType = CStr(fieldTypeComboBox.SelectedItem)
        End Sub
    
        Private Sub innerTextBoxForGroupComboBox_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
            mainWindow.PresentationModel.FieldGroup = groupComboBox.Text
        End Sub
    
        Private Sub nameTextBox_TextChanged(ByVal sender As Object, ByVal e As TextChangedEventArgs)
            mainWindow.PresentationModel.FieldName = nameTextBox.Text
        End Sub
    End Class
    
    using System.Windows;
    using System.Windows.Controls;
    
    namespace ProjectTemplateWizard
    {
        public partial class Page2 : UserControl
        {
            private WizardWindow mainWindow;
            private TextBox innerTextBoxForGroupComboBox;
    
            internal Page2(WizardWindow mainWindow)
            {
                this.mainWindow = mainWindow;
                InitializeComponent();
            }
    
            internal void ClearControls()
            {
                fieldTypeComboBox.Items.Clear();
                groupComboBox.Items.Clear();
                nameTextBox.Clear();
            }
    
            internal void PopulateSiteColumnOptions()
            {
                // Add the available field type names to the combo box.
                System.Collections.ArrayList fieldTypes = mainWindow.PresentationModel.GetFieldTypes();
                if (fieldTypes != null)
                {
                    fieldTypes.Sort();
                    foreach (string fieldValue in fieldTypes)
                    {
                        fieldTypeComboBox.Items.Add(fieldValue);
                    }
    
                    fieldTypeComboBox.SelectedIndex = 0;
                }
    
                // Add the default group names to the combo box.
                System.Collections.Generic.List<string> fieldGroups = mainWindow.PresentationModel.GetFieldGroups();
                foreach (string fieldGroupValue in fieldGroups)
                {
                    groupComboBox.Items.Add(fieldGroupValue);
                }
    
                groupComboBox.SelectedIndex = 0;
            }
    
            private void UserControl_Loaded(object sender, RoutedEventArgs e)
            {
                // Handle the TextChanged event of the underlying TextBox for the ComboBox. This enables us to determine 
                // 1) when the user selects an item in the list and 2) when they type their own custom group name. 
                // The ComboBox.SelectionChanged event is not raised when you type in an editable ComboboBox.
                innerTextBoxForGroupComboBox = groupComboBox.Template.FindName(
                    "PART_EditableTextBox", groupComboBox) as TextBox;
                innerTextBoxForGroupComboBox.TextChanged += innerTextBoxForGroupComboBox_TextChanged;
            }
    
            private void fieldTypeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                mainWindow.PresentationModel.FieldType = (string)fieldTypeComboBox.SelectedItem;
            }
    
            void innerTextBoxForGroupComboBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                mainWindow.PresentationModel.FieldGroup = groupComboBox.Text;
            }
    
            private void nameTextBox_TextChanged(object sender, TextChangedEventArgs e)
            {
                mainWindow.PresentationModel.FieldName = nameTextBox.Text;
            }
        }
    }
    

Implementar el asistente

Para definir la funcionalidad principal del asistente, implemente la interfaz IWizard. Esta interfaz define los métodos que Visual Studio llama cuando el asistente se inicia y finaliza y, a veces, mientras se ejecuta.

Para implementar el asistente

  1. En el proyecto ProjectTemplateWizard, abra el archivo de código SiteColumnProjectWizard.

  2. Reemplace todo el contenido de este archivo por el código siguiente:

    Imports EnvDTE
    Imports Microsoft.VisualStudio.SharePoint
    Imports Microsoft.VisualStudio.TemplateWizard
    Imports System
    Imports System.Collections.Generic
    
    Public Class SiteColumnProjectWizard
        Implements IWizard
    
        Private wizardUI As WizardWindow
        Private dteObject As DTE
        Private presentationModel As SiteColumnWizardModel
        Private signingManager As ProjectSigningManager
    
        Public Sub New()
            signingManager = New ProjectSigningManager()
        End Sub
    
        Public Sub RunStarted(ByVal automationObject As Object, ByVal replacementsDictionary As Dictionary(Of String, String), _
            ByVal runKind As WizardRunKind, ByVal customParams() As Object) Implements IWizard.RunStarted
    
            dteObject = CType(automationObject, DTE)
            presentationModel = New SiteColumnWizardModel(dteObject, False)
    
            If Not presentationModel.ProjectService.IsSharePointInstalled Then
                Dim errorString As String = "A SharePoint server is not installed on this computer. A SharePoint server " &
                    "must be installed to work with SharePoint projects."
                System.Windows.MessageBox.Show(errorString, "SharePoint Not Installed", System.Windows.MessageBoxButton.OK,
                    System.Windows.MessageBoxImage.Error)
                Throw New WizardCancelledException(errorString)
            End If
    
            wizardUI = New WizardWindow(presentationModel)
            Dim dialogCompleted? As Boolean = wizardUI.ShowModal()
    
            If (dialogCompleted = True) Then
                replacementsDictionary.Add("$selectedfieldtype$", presentationModel.FieldType)
                replacementsDictionary.Add("$selectedgrouptype$", presentationModel.FieldGroup)
                replacementsDictionary.Add("$fieldname$", presentationModel.FieldName)
                signingManager.GenerateKeyFile()
            Else
                Throw New WizardCancelledException()
            End If
        End Sub
    
        ' Populate the SiteUrl and IsSandboxedSolution properties in the new project, and add a new 
        ' key.snk file to the project.
        Public Sub ProjectFinishedGenerating(ByVal project As Project) _
            Implements IWizard.ProjectFinishedGenerating
            Dim sharePointProject As ISharePointProject = presentationModel.ProjectService.Convert(Of Project, ISharePointProject)(project)
            sharePointProject.SiteUrl = New Uri(presentationModel.CurrentSiteUrl, UriKind.Absolute)
            sharePointProject.IsSandboxedSolution = presentationModel.IsSandboxed
            signingManager.AddKeyFile(project)
        End Sub
    
        ' Always return true; this IWizard implementation throws a WizardCancelledException
        ' that is handled by Visual Studio if the user cancels the wizard.
        Public Function ShouldAddProjectItem(ByVal filePath As String) As Boolean _
            Implements IWizard.ShouldAddProjectItem
            Return True
        End Function
    
        ' The following IWizard methods are not used in this example.
        Public Sub BeforeOpeningFile(ByVal projectItem As ProjectItem) _
            Implements IWizard.BeforeOpeningFile
        End Sub
    
        Public Sub ProjectItemFinishedGenerating(ByVal projectItem As ProjectItem) _
            Implements IWizard.ProjectItemFinishedGenerating
        End Sub
    
        Public Sub RunFinished() Implements IWizard.RunFinished
        End Sub
    End Class
    
    using EnvDTE;
    using Microsoft.VisualStudio.SharePoint;
    using Microsoft.VisualStudio.TemplateWizard;
    using System;
    using System.Collections.Generic;
    
    namespace ProjectTemplateWizard
    {
        public class SiteColumnProjectWizard : IWizard
        {
            private WizardWindow wizardUI;
            private DTE dteObject;
            private SiteColumnWizardModel presentationModel;
            private ProjectSigningManager signingManager;
    
            public SiteColumnProjectWizard()
            {
                signingManager = new ProjectSigningManager();
            }
    
            public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, 
                WizardRunKind runKind, object[] customParams)
            {
                dteObject = automationObject as DTE;
                presentationModel = new SiteColumnWizardModel(dteObject, false);
    
                if (!presentationModel.ProjectService.IsSharePointInstalled)
                {
                    string errorString = "A SharePoint server is not installed on this computer. A SharePoint server " +
                        "must be installed to work with SharePoint projects.";
                    System.Windows.MessageBox.Show(errorString, "SharePoint Not Installed", System.Windows.MessageBoxButton.OK,
                        System.Windows.MessageBoxImage.Error);
                    throw new WizardCancelledException(errorString);
                }
    
                wizardUI = new WizardWindow(presentationModel);
                Nullable<bool> dialogCompleted = wizardUI.ShowModal();
    
                if (dialogCompleted == true)
                {
                    replacementsDictionary.Add("$selectedfieldtype$", presentationModel.FieldType);
                    replacementsDictionary.Add("$selectedgrouptype$", presentationModel.FieldGroup);
                    replacementsDictionary.Add("$fieldname$", presentationModel.FieldName);
                    signingManager.GenerateKeyFile();
                }
                else
                {
                    throw new WizardCancelledException();
                }
            }
    
            // Populate the SiteUrl and IsSandboxedSolution properties in the new project, and add a new 
            // key.snk file to the project.
            public void ProjectFinishedGenerating(Project project)
            {
                ISharePointProject sharePointProject = presentationModel.ProjectService.Convert<Project, ISharePointProject>(project);
                sharePointProject.SiteUrl = new Uri(presentationModel.CurrentSiteUrl, UriKind.Absolute);
                sharePointProject.IsSandboxedSolution = presentationModel.IsSandboxed;
                signingManager.AddKeyFile(project);
            }
    
            // Always return true; this IWizard implementation throws a WizardCancelledException
            // that is handled by Visual Studio if the user cancels the wizard.
            public bool ShouldAddProjectItem(string filePath)
            {
                return true;
            }
    
            // The following IWizard methods are not used in this example.
            public void BeforeOpeningFile(ProjectItem projectItem)
            {
            }
    
            public void ProjectItemFinishedGenerating(ProjectItem projectItem)
            {
            }
    
            public void RunFinished()
            {
            }
        }
    }
    

Crear los comandos de SharePoint

Cree dos comandos personalizados que llamen al modelo de objetos de servidor de SharePoint. Un comando determina si la dirección URL del sitio que el usuario escribe en el asistente es válida. El otro comando obtiene todos los tipos de campo del sitio de SharePoint especificado para que los usuarios puedan seleccionar el que desean usar como base para la nueva columna de sitio.

Para definir los comandos de SharePoint

  1. En el proyecto SharePointCommands, abra el archivo de código Commands.

  2. Reemplace todo el contenido de este archivo por el código siguiente:

    Imports Microsoft.SharePoint
    Imports Microsoft.VisualStudio.SharePoint.Commands
    
    Namespace Contoso.SharePoint.Commands
    
        Friend Class Commands
    
            <SharePointCommand(CommandIds.ValidateSite)> _
            Private Function ValidateSite(ByVal context As ISharePointCommandContext, ByVal url As Uri) As Boolean
                Using site As SPSite = New SPSite(url.AbsoluteUri)
                    Dim webUrl As String = DetermineWebUrl(url.AbsolutePath, site.ServerRelativeUrl)
                    If webUrl IsNot Nothing Then
                        Using web As SPWeb = site.OpenWeb(webUrl, True)
                            Return web.Exists
                        End Using
                    End If
                End Using
                Return False
            End Function
    
            ' For simplicity, this command does not check to make sure the provided Uri is valid. 
            ' Use the ValidateSite command to verify that the Uri is valid first.
            <SharePointCommand(CommandIds.GetFieldTypes)> _
            Private Function GetFieldTypes(ByVal context As ISharePointCommandContext, ByVal url As Uri) As String()
                Dim columnDefinitions As List(Of String) = New List(Of String)()
                Using site As SPSite = New SPSite(url.AbsoluteUri)
                    Dim webUrl As String = DetermineWebUrl(url.AbsolutePath, site.ServerRelativeUrl)
                    Using web As SPWeb = site.OpenWeb(webUrl, True)
                        For Each columnDefinition As SPFieldTypeDefinition In web.FieldTypeDefinitionCollection
                            columnDefinitions.Add(columnDefinition.TypeName)
                        Next
                        ' SharePoint commands cannot serialize List<string>, so return an array.
                        Return columnDefinitions.ToArray()
                    End Using
                End Using
            End Function
    
            Private Function DetermineWebUrl(ByVal serverRelativeInputUrl As String, ByVal serverRelativeSiteUrl As String) As String
                ' Make sure both URLs have a trailing slash.
                serverRelativeInputUrl = EnsureTrailingSlash(serverRelativeInputUrl)
                serverRelativeSiteUrl = EnsureTrailingSlash(serverRelativeSiteUrl)
    
                Dim webUrl As String = Nothing
                Dim isSubString As Boolean = serverRelativeInputUrl.StartsWith(serverRelativeSiteUrl, StringComparison.OrdinalIgnoreCase)
    
                If isSubString Then
                    ' The Web URL cannot have escaped characters.
                    webUrl = Uri.UnescapeDataString(serverRelativeInputUrl.Substring(serverRelativeSiteUrl.Length))
                End If
                Return webUrl
            End Function
    
            Private Function EnsureTrailingSlash(ByVal url As String)
                If Not String.IsNullOrEmpty(url) AndAlso url(url.Length - 1) <> "/" Then
                    url += "/"
                End If
                Return url
            End Function
        End Class
    End Namespace
    
    using System;
    using System.Collections.Generic;
    using Microsoft.SharePoint;
    using Microsoft.VisualStudio.SharePoint.Commands;
    
    namespace Contoso.SharePoint.Commands
    {
        internal class Commands
        {
            [SharePointCommand(CommandIds.ValidateSite)]
            private bool ValidateSite(ISharePointCommandContext context, Uri url)
            {
                using (SPSite site = new SPSite(url.AbsoluteUri))
                {
                    string webUrl = DetermineWebUrl(url.AbsolutePath, site.ServerRelativeUrl);
                    if (webUrl != null)
                    {
                        using (SPWeb web = site.OpenWeb(webUrl, true))
                        {
                            return web.Exists;
                        }
                    }
                }
    
                return false;
            }
    
            // For simplicity, this command does not check to make sure the provided Uri is valid. 
            // Use the ValidateSite command to verify that the Uri is valid first.
            [SharePointCommand(CommandIds.GetFieldTypes)]
            private string[] GetFieldTypes(ISharePointCommandContext context, Uri url)
            {
                List<string> columnDefinitions = new List<string>();
                using (SPSite site = new SPSite(url.AbsoluteUri))
                {
                    string webUrl = DetermineWebUrl(url.AbsolutePath, site.ServerRelativeUrl);
                    using (SPWeb web = site.OpenWeb(webUrl, true))
                    {
                        foreach (SPFieldTypeDefinition columnDefinition in web.FieldTypeDefinitionCollection)
                        {
                            columnDefinitions.Add(columnDefinition.TypeName);
                        }
    
                        // SharePoint commands cannot serialize List<string>, so return an array.
                        return columnDefinitions.ToArray();
                    }
                }
            }
    
            private string DetermineWebUrl(string serverRelativeInputUrl, string serverRelativeSiteUrl)
            {
                // Make sure both URLs have a trailing slash.
                serverRelativeInputUrl = EnsureTrailingSlash(serverRelativeInputUrl);
                serverRelativeSiteUrl = EnsureTrailingSlash(serverRelativeSiteUrl);
    
                string webUrl = null;
                bool isSubString = serverRelativeInputUrl.StartsWith(serverRelativeSiteUrl, StringComparison.OrdinalIgnoreCase);
    
                if (isSubString)
                {
                    // The Web URL cannot have escaped characters.
                    webUrl = Uri.UnescapeDataString(serverRelativeInputUrl.Substring(serverRelativeSiteUrl.Length));
                }
    
                return webUrl;
            }
    
            private string EnsureTrailingSlash(string url)
            {
                if (!String.IsNullOrEmpty(url)
                    && url[url.Length - 1] != '/')
                {
                    url += '/';
                }
                return url;
            }
        }
    }
    

Punto de control

En este punto del tutorial, todo el código del asistente está en el proyecto. Compile el proyecto para asegurarse de que se compila sin errores.

Para generar el proyecto

  • En el menú Generar, seleccione Generar solución.

Quitar el archivo key.snk de la plantilla de proyecto

En el Tutorial: Crear un elemento de proyecto de columna de sitio con una plantilla de proyecto, parte 1, la plantilla de proyecto creada contiene un archivo key.snk que se usa para firmar cada instancia del proyecto de columna de sitio. Este archivo key.snk ya no es necesario porque el asistente genera ahora un nuevo archivo key.snk para cada proyecto. Quite el archivo key.snk de la plantilla de proyecto y quite las referencias a este archivo.

Para quitar el archivo key.snk de la plantilla de proyecto

  1. En el Explorador de soluciones, en el nodo SiteColumnProjectTemplate, haga clic con el botón secundario en el archivo key.snk y haga clic en Eliminar. En el cuadro de mensaje que le pide confirmación, haga clic en Aceptar.

  2. En el nodo SiteColumnProjectTemplate, abra el archivo SiteColumnProjectTemplate.vstemplate.

  3. Quite el elemento siguiente del archivo.

    <ProjectItem ReplaceParameters="false" TargetFileName="key.snk">key.snk</ProjectItem>
    
  4. Guarde y cierre el archivo.

  5. En el nodo SiteColumnProjectTemplate, abra el archivo ProjectTemplate.csproj o ProjectTemplate.vbproj.

  6. Quite el siguiente elemento PropertyGroup.

    <PropertyGroup>
      <SignAssembly>true</SignAssembly>
      <AssemblyOriginatorKeyFile>key.snk</AssemblyOriginatorKeyFile>
    </PropertyGroup>
    
  7. Quite el siguiente elemento None.

    <None Include="key.snk" />
    
  8. Guarde y cierre el archivo.

Asociar el asistente a la plantilla de proyecto

Ahora que ha implementado el asistente, debe asociarlo a la plantilla de proyecto Columna de sitio. Hay tres procedimientos principales que debe completar para ello:

  1. Firmar el ensamblado del asistente con un nombre seguro.

  2. Obtener el token de clave pública del ensamblado del asistente.

  3. Agregue una referencia al ensamblado del asistente en el archivo .vstemplate de la plantilla de proyecto de Columna de sitio.

Para firmar el ensamblado del asistente con un nombre seguro

  1. En el Explorador de soluciones, haga clic con el botón secundario en el nodo de proyecto ProjectTemplateWizard y haga clic en Propiedades.

  2. Haga clic en la ficha Firma.

  3. Active la casilla Firmar el ensamblado.

  4. En la lista desplegable Elija un archivo de clave de nombre completo, seleccione <Nuevo...>.

  5. En el cuadro de diálogo Crear clave de nombre seguro, escriba un nombre para el nuevo archivo de clave y desactive la casilla Proteger mi archivo de clave mediante contraseña.

  6. Haga clic en Aceptar.

  7. En el menú Generar, seleccione Generar solución.

Para obtener el token de clave pública del ensamblado del asistente

  1. Abra una ventana Símbolo del sistema de Visual Studio.

  2. Ejecute el siguiente comando. Reemplace la ruta de acceso al ensamblado del asistente por la ruta de acceso completa al ensamblado ProjectTemplateWizard.dll compilado del proyecto ProjectTemplateWizard en el equipo de desarrollo.

    sn.exe -T path to wizard assembly
    

    El token de clave pública del ensamblado ProjectTemplateWizard.dll se escribe en la ventana del símbolo del sistema de Visual Studio.

  3. Mantenga abierta la ventana del símbolo del sistema de Visual Studio. Necesitará el token de clave pública durante el procedimiento siguiente.

Para agregar una referencia al ensamblado del asistente del archivo .vstemplate

  1. En el Explorador de soluciones, expanda el nodo del proyecto SiteColumnProjectTemplate y abra el archivo SiteColumnProjectTemplate.vstemplate.

  2. Cuando se acerque el final del archivo, agregue el siguiente elemento WizardExtension entre las etiquetas </TemplateContent> y </VSTemplate>. Reemplace el valor de su token del atributo PublicKeyToken por el token de clave pública que obtuvo en el procedimiento anterior.

    <WizardExtension>
      <Assembly>ProjectTemplateWizard, Version=1.0.0.0, Culture=neutral, PublicKeyToken=your token</Assembly>
      <FullClassName>ProjectTemplateWizard.SiteColumnProjectWizard</FullClassName>
    </WizardExtension>
    

    Para obtener más información sobre el elemento WizardExtension, vea WizardExtension (Elemento, Plantillas de Visual Studio).

  3. Guarde y cierre el archivo.

Agregar parámetros reemplazables al archivo Elements.xml de la plantilla de proyecto

Agregue varios parámetros reemplazables al archivo Elements.xml del proyecto SiteColumnProjectTemplate. Estos parámetros se inicializan en el método RunStarted de la clase SiteColumnProjectWizard que definió anteriormente. Cuando un usuario crea un proyecto de columna de sitio, Visual Studio reemplaza automáticamente estos parámetros en el archivo Elements.xml del nuevo proyecto por los valores especificados en el asistente.

Un parámetro reemplazable es un token que empieza y termina por el carácter del signo de dólar ($). Además de definir sus propios parámetros reemplazables, puede usar los parámetros integrados que se definen y se inicializan en el sistema de proyectos de SharePoint. Para obtener más información, vea Parámetros reemplazables.

Para agregar parámetros reemplazables al archivo Elements.xml

  1. Abra el archivo Elements.xml del proyecto SiteColumnProjectTemplate.

  2. Reemplace el contenido de este archivo por el código XML siguiente:

    <?xml version="1.0" encoding="utf-8"?>
    <Elements xmlns="https://schemas.microsoft.com/sharepoint/">
      <Field ID="{$guid5$}" 
             Name="$fieldname$" 
             DisplayName="$fieldname$" 
             Type="$selectedfieldtype$" 
             Group="$selectedgrouptype$">
      </Field>
    </Elements>
    

    El nuevo XML cambia los valores de los atributos Name, DisplayName, Type y Group por parámetros reemplazables personalizados.

  3. Guarde y cierre el archivo.

Agregar el asistente al paquete VSIX

Para implementar el asistente con el paquete VSIX que contiene la plantilla de proyecto de columna de sitio, agregue las referencias del proyecto de asistente y del proyecto de comandos de SharePoint al archivo source.extension.vsixmanifest del proyecto VSIX.

Para agregar el asistente al paquete VSIX

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

    Visual Studio abre el archivo en el editor de manifiestos.

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

  3. En el cuadro de diálogo Agregar contenido, en el cuadro de lista Seleccione un tipo de contenido, seleccione Asistente para plantillas.

  4. En Seleccione un origen, haga clic en el botón de radio Proyecto y seleccione ProjectTemplateWizard en el cuadro de lista adyacente.

  5. Haga clic en Aceptar.

  6. En el editor de manifiestos, haga clic de nuevo en el botón Agregar contenido.

  7. En el cuadro de diálogo Agregar contenido, en el cuadro de lista Seleccione un tipo de contenido, seleccione Tipo de extensión personalizada.

    Nota

    Este valor corresponde al elemento CustomExtension del archivo extension.vsixmanifest. Este elemento especifica una extensión personalizada que desea incluir en la extensión de Visual Studio. Para obtener más información, vea CustomExtension Element (VSX Schema).

  8. En el cuadro de texto Tipo, escriba SharePoint.Commands.v4.

    Nota

    Este valor corresponde al atributo Type del elemento CustomExtension del archivo extension.vsixmanifest. El valor Sharepoint.Commands.v4 se requiere para todos los ensamblados de la extensión personalizada que contengan comandos de SharePoint personalizados.

  9. En Seleccione un origen, haga clic en el botón de radio Proyecto, y seleccione SharePointCommands en el cuadro de lista al lado de él.

  10. Haga clic en Aceptar.

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

Probar el asistente

Ahora está preparado para probar el asistente. Primero, empiece a depurar la solución SiteColumnProjectItem en la instancia experimental de Visual Studio. A continuación, pruebe el asistente para el proyecto de columna de sitio en la instancia experimental de Visual Studio. Por último, compile y ejecute el proyecto para comprobar que la columna de sitio funciona del modo esperado.

Para empezar a depurar la solución

  1. Reinicie Visual Studio con privilegios de administrador y abra la solución SiteColumnProjectItem.

  2. En el proyecto ProjectTemplateWizard, abra el archivo de código de SiteColumnProjectWizard y agregue un punto de interrupción a la primera línea de código en el método RunStarted.

  3. En el menú Depurar, haga clic en Excepciones.

  4. En el cuadro de diálogo Excepciones, asegúrese de que las casillas Producida y No controlada por el usuario de Excepciones de Common Language Runtime están desactivadas.

  5. Haga clic en Aceptar.

  6. Presione F5 para iniciar la depuración.

    Visual Studio instala la extensión para %UserProfile%\AppData\Local\Microsoft\VisualStudio\10.0Exp\Extensions\Contoso\Columna de sitio\1 .0 e inicia una instancia experimental de Visual Studio. Probará el elemento de proyecto en esta instancia de Visual Studio.

Para probar el asistente en Visual Studio

  1. En la instancia experimental de Visual Studio, en el menú Archivo, seleccione Nuevo y haga clic en Proyecto.

  2. Expanda Visual C# o Visual Basic (en función del lenguaje que admita la plantilla de proyecto), expanda SharePoint y, a continuación, haga clic en 2010.

  3. En la lista de plantillas de proyecto, haga clic en Columna de sitio.

  4. En el cuadro Nombre, escriba SiteColumnWizardTest.

  5. Haga clic en Aceptar.

  6. Compruebe que el código de la otra instancia de Visual Studio se detiene en el punto de interrupción que estableció anteriormente en el método RunStarted. Presione F5 para continuar y depurar el proyecto.

  7. En el Asistente para la personalización de SharePoint, escriba la dirección URL del sitio que desea usar en la depuración y haga clic en Siguiente.

  8. En la segunda página del Asistente para la personalización de SharePoint, realice las selecciones siguientes:

    • En Tipo, seleccione Booleano en la lista desplegable.

    • En Grupo, escriba Columnas Sí/No personalizadas en el cuadro combinado.

    • En Nombre, escriba Mi columna Sí/No en el cuadro de texto.

  9. Haga clic en Finalizar.

    Aparece un nuevo proyecto en el Explorador de soluciones con un elemento de proyecto denominado Field1 y Visual Studio abre el archivo Elements.xml en el editor. Compruebe que Elements.xml contiene los valores especificados en el asistente.

Para probar la columna de sitio de SharePoint

  1. En la instancia experimental de Visual Studio, presione F5. La columna de sitio se empaqueta e implementa en el sitio de SharePoint especificado por la propiedad Site URL del proyecto. El explorador web se abre a la página predeterminado de este sitio.

    Nota

    Si aparece el cuadro de diálogo Depuración de scripts deshabilitada, haga clic en para continuar depurando el proyecto.

  2. En el menú Acciones del sitio, haga clic en Configuración del sitio.

  3. En Galerías, haga clic en Columnas de sitio.

  4. En la lista de columnas de sitio, compruebe que haya un grupo Columnas Sí/No personalizadas que contiene una columna denominada Mi columna Sí/No.

  5. Cierre el explorador web.

Limpiar el equipo de desarrollo

Después de probar el elemento de proyecto, quite la plantilla de proyecto de la instancia experimental de Visual Studio.

para limpiar el equipo de desarrollo

  1. En la instancia experimental de Visual Studio, en el menú Herramientas, haga clic en Administrador de extensiones.

    Se abre el cuadro de diálogo Administrador de extensiones.

  2. En la lista de extensiones, haga clic en Columna de sitio y, a continuación, en Desinstalar.

  3. En el cuadro de diálogo que aparece, haga clic en para confirmar la desinstalación.

  4. Haga clic en Reiniciar ahora para completar la desinstalación.

  5. Cierre ambas instancias de Visual Studio (la instancia experimental y la instancia de Visual Studio que tiene abierta la solución SiteColumnProjectItem).

Vea también

Tareas

Cómo: Utilizar los asistentes con las plantillas de proyectos

Referencia

Referencia de esquema de plantillas de Visual Studio

IWizard

Otros recursos

Tutorial: Crear un elemento de proyecto de columna de sitio con una plantilla de proyecto, parte 1

Definir tipos de elementos de proyecto personalizados de SharePoint

Crear plantillas de elemento y plantillas de proyecto para los elementos de proyecto de SharePoint