Contribuciones y configuraciones

Puede exponer componentes de extensiones a Visual Studio derivando de determinadas clases base y puede configurarlos definiendo determinadas propiedades y usando varios atributos.

Contribuciones de Visual Studio

El propósito de una extensión de Visual Studio es contribuir con nuevas características a Visual Studio. Esto se logra mediante la extensión de una de muchas clases como Command, ToolWindowo ExtensionPart y la aplicación del VisualStudioContribution atributo .

En este artículo se hace referencia a la extensión de ejemplo Command Parenting para explicar los conceptos de contribución y configuración de componentes de extensión.

Cada extensión de extensibilidad de VisualStudio.Extensibility debe contribuir al menos a una Extension clase:

namespace CommandParentingSample;

[VisualStudioContribution]
public class CommandParentingSampleExtension : Extension
{
    /// <inheritdoc/>
    protected override void InitializeServices(IServiceCollection serviceCollection)
    {
        base.InitializeServices(serviceCollection);
    }
}

La Extension clase es la primera clase creada por instancias de la extensión y permite agregar sus propios servicios al IServiceCollection que se va a usar para la inserción de dependencias.

El ejemplo De creación de elementos primarios de comandos contribuye a otra clase, un Commandobjeto , a Visual Studio:

[VisualStudioContribution]
internal class SampleCommand : Command
{
    public SampleCommand()
    {
    }
    ...

Al extender una clase base proporcionada por el SDK de extensibilidad de VisualStudio.Extensibility, puede saber si se espera que use el VisualStudioContribution atributo comprobando si la clase base implementa IVisualStudioContributionClass (y CommandExtension sí).

Las clases de contribución de Visual Studio se crean instancias diferidas de singletons: solo se crea una instancia y se retrasa su creación hasta que Visual Studio necesita interactuar con él (por ejemplo, cuando el usuario invoca por primera vez una Command instancia).

La infraestructura de extensibilidad de VisualStudio.Extensibility también permite recibir servicios a través de la inserción de dependencias como parámetros de constructor de las clases de contribución de Visual Studio (consulte Servicios proporcionados por SDK para la inserción), incluido cualquier servicio que haya agregado al IServiceCollection método de la Extension clase InitializeServices .

Visual Studio a menudo requiere un identificador único para asociarse a las contribuciones. En la mayoría de los casos, la infraestructura de extensibilidad de VisualStudio.Extensibility usa el nombre completo de la clase de contribución de Visual Studio como identificador de contribución. Por ejemplo, el identificador de la Extension clase anterior sería CommandParentingSample.CommandParentingSampleExtension. Es posible que quiera elegir cuidadosamente el nombre de tipo y el espacio de nombres de las clases de contribución de Visual Studio, ya que pueden aparecer en los registros y mensajes de error de Visual Studio.

Configuración de contribuciones de Visual Studio

La mayoría de las clases de contribución de Visual Studio requieren o permiten la configuración. Por ejemplo, la Command clase abstracta requiere la implementación de una CommandConfiguration propiedad que especifica al menos el nombre para mostrar del comando y, opcionalmente, otras propiedades como su ubicación.

[VisualStudioContribution]
internal class SampleCommand : Command
{
    /// <inheritdoc />
    public override CommandConfiguration CommandConfiguration => new("%CommandParentingSample.SampleCommand.DisplayName%")
    {
        Placements = new[]
        {
            // File in project context menu
            CommandPlacement.VsctParent(new Guid("{d309f791-903f-11d0-9efc-00a0c911004f}"), id: 1072, priority: 0),

            // Project context menu
            CommandPlacement.VsctParent(new Guid("{d309f791-903f-11d0-9efc-00a0c911004f}"), id:  1026, priority: 0),

            // Solution context menu
            CommandPlacement.VsctParent(new Guid("{d309f791-903f-11d0-9efc-00a0c911004f}"), id:  1043, priority: 0),
        },
    };
    ...

CommandConfiguration es una constante en tiempo de compilación, lo que significa que su valor se evalúa cuando se compila la extensión y se incluye en el manifiesto de extensión (extension.json). Visual Studio puede leer el manifiesto de extensión sin cargar la propia extensión, lo que permite un mejor rendimiento.

Las constantes en tiempo de compilación están sujetas a limitaciones adicionales en comparación con las propiedades normales, por ejemplo, deben ser de solo lectura y su código de inicialización no puede incluir referencias a miembros no estáticos o bloques de código imperativo de varias instrucciones. Estas restricciones las aplican las herramientas de compilación de VisualStudio.Extensibility y producen mensajes de error como los siguientes:

Se encontró un problema al evaluar la constante en tiempo de compilación SampleCommand.CommandConfiguration. No se admiten referencias a miembros no estáticos definidos por el usuario al evaluar valores constantes en tiempo de compilación.

En general, la extensión no debe hacer referencia a las propiedades de configuración de constantes en tiempo de compilación en tiempo de ejecución.

Puede identificar fácilmente las propiedades de configuración de constantes en tiempo de compilación, ya que su definición tiene el CompileTimeEvaluation atributo .

public abstract class Command : ExecutableCommandHandler, IVisualStudioContributionClass
{
    ...
    /// <summary>
    /// Gets the configuration for this command. The value of this property is evaluated at compile time
    /// when building the Visual Studio extension.
    /// </summary>
    [CompileTimeEvaluation]
    public abstract CommandConfiguration CommandConfiguration { get; }
    ...

En raras ocasiones, las propiedades de configuración pueden ser opcionales. En determinados casos, es posible que tenga que implementar varias propiedades de configuración en la misma clase. Esto es común al extender ExtensionPart e implementar varias interfaces, cada una de las cuales requiere su propia propiedad de configuración.

Propiedades de configuración independientes

Como se ha descrito anteriormente, las clases de contribución de Visual Studio definen una clase singleton que normalmente expone una o varias propiedades de configuración constantes en tiempo de compilación. Los valores de las propiedades de configuración se guardan como metadatos de extensión.

Algunas características de extensibilidad requieren que se especifiquen metadatos de extensión que no están vinculados a ninguna clase y que sea significativo por sí solos o que otras configuraciones hagan referencia a ellos. Algunos ejemplos son las definiciones de menú, barra de herramientas y tipo de documento. Esto se logra aplicando el VisualStudioContribution atributo a una propiedad de configuración estática de solo lectura.

Las propiedades de contribución de Visual Studio se pueden colocar en cualquier clase.

En el ejemplo Primario de comandos se define una barra de herramientas mediante la declaración de una propiedad estática de tipo ToolbarConfiguration y la marca como VisualStudioContribution.

namespace CommandParentingSample;

internal static class ExtensionCommandConfiguration
{
    [VisualStudioContribution]
    public static ToolbarConfiguration ToolBar => new("%CommandParentingSample.ToolBar.DisplayName%")
    {
        Children = new[]
        {
            ToolbarChild.Command<SampleCommand>(),
        },
    };
}

Las propiedades de contribución de Visual Studio también son constantes en tiempo de compilación y están sujetas a las mismas limitaciones descritas anteriormente.

Una propiedad de contribución de Visual Studio también puede hacer referencia a otra propiedad de configuración. Por ejemplo:

public static class MenuConfigurations
{
    [VisualStudioContribution]
    public static CommandGroupConfiguration MyCommandGroup => new(GroupPlacement.KnownPlacements.ExtensionsMenu)
    {
        Children = new GroupChild[]
        {
            GroupChild.Menu(MyMenu),
        },
    };

    [VisualStudioContribution]
    public static MenuConfiguration MyMenu => new("%MyMenu.DisplayName%")
    {
        Children = new[]
        {
            MenuChild.Command<MyCommand>(),
        },
    };
    ...

Los tipos destinados a usarse para definir las propiedades de contribución de Visual Studio implementan la IVisualStudioContributionProperty interfaz y se marcan con el CompileTimeEvaluation atributo para documentar que sus valores se evalúan cuando se compila la extensión.

[CompileTimeEvaluation]
public sealed class DocumentTypeConfiguration : IVisualStudioContributionProperty ...

Las instrucciones sobre no hacer referencia a las propiedades de configuración de constantes en tiempo de compilación en tiempo de ejecución también se aplican a las propiedades de contribución de Visual Studio.

En caso de que se requiera un identificador único para una propiedad de contribución de Visual Studio, la infraestructura de extensibilidad usa su nombre completo (que contiene el nombre completo del tipo y el nombre de propiedad) como identificador. Por ejemplo, el identificador único de la configuración de la barra de herramientas que se describe aquí sería CommandParentingSample.ExtensionCommandConfiguration.ToolbarConfiguration.