Contributi e configurazioni

È possibile esporre i componenti delle estensioni a Visual Studio derivando da determinate classi di base ed è possibile configurarli definendo determinate proprietà e usando vari attributi.

Contributi di Visual Studio

Lo scopo di un'estensione di Visual Studio è contribuire a nuove funzionalità in Visual Studio. Questa operazione viene ottenuta estendendo una di molte classi, ad Commandesempio , ToolWindowo e ExtensionPart applicando l'attributo VisualStudioContribution .

Questo articolo fa riferimento all'estensione di esempio Command Parenting per illustrare i concetti relativi ai contributi e alla configurazione dei componenti di estensione.

Ogni estensione VisualStudio.Extensibility deve contribuire almeno a una Extension classe:

namespace CommandParentingSample;

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

La Extension classe è la prima classe di cui è stata creata un'istanza dell'estensione e consente di aggiungere servizi personalizzati a da usare per l'inserimento IServiceCollection delle dipendenze.

L'esempio Command Parenting contribuisce a un'altra classe, a Command, in Visual Studio:

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

Quando si estende una classe di base fornita da VisualStudio.Extensibility SDK, è possibile sapere se si prevede di usare l'attributo VisualStudioContribution verificando se la classe di base implementa IVisualStudioContributionClass (sia Extension che Command fanno).

Le classi di contributo di Visual Studio vengono create con un'istanza differita: viene creata una sola istanza e la relativa creazione viene ritardata fino a quando Visual Studio non deve interagire con esso , ad esempio quando un Command oggetto viene richiamato per la prima volta dall'utente.

L'infrastruttura VisualStudio.Extensibility consente anche di ricevere servizi tramite l'inserimento delle dipendenze come parametri del costruttore delle classi di contributo di Visual Studio (vedere Servizi forniti dall'SDK per l'inserimentoIServiceCollection), incluso qualsiasi servizio aggiunto a nel metodo della Extension classe . InitializeServices

Visual Studio richiede spesso un identificatore univoco da associare ai contributi. Nella maggior parte dei casi, l'infrastruttura VisualStudio.Extensibility usa il nome completo della classe di contributo di Visual Studio come identificatore del contributo. Ad esempio, l'identificatore della Extension classe precedente sarebbe CommandParentingSample.CommandParentingSampleExtension. È consigliabile scegliere con attenzione il nome del tipo e lo spazio dei nomi delle classi di contributo di Visual Studio, perché potrebbero essere visualizzati nei log di Visual Studio e nei messaggi di errore.

Configurazione dei contributi di Visual Studio

La maggior parte delle classi di contributi di Visual Studio richiede o consente la configurazione. Ad esempio, la Command classe astratta richiede l'implementazione di una CommandConfiguration proprietà specificando almeno il nome visualizzato del comando e, facoltativamente, altre proprietà come la relativa posizione.

[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 è una costante in fase di compilazione, il che significa che il relativo valore viene valutato quando viene compilata l'estensione ed è incluso nel manifesto dell'estensione (extension.json). Visual Studio può leggere il manifesto dell'estensione senza caricare l'estensione stessa, consentendo prestazioni migliori.

Le costanti in fase di compilazione sono soggette a limitazioni aggiuntive rispetto alle normali proprietà, ad esempio devono essere di sola lettura e il codice di inizializzazione non può includere riferimenti a membri non statici o blocchi di codice imperativi con più istruzioni. Queste restrizioni vengono applicate dagli strumenti di compilazione VisualStudio.Extensibility e genera messaggi di errore simili ai seguenti:

Si è verificato un problema durante la valutazione della costante in fase di compilazione SampleCommand.CommandConfiguration. I riferimenti ai membri non statici definiti dall'utente non sono supportati durante la valutazione dei valori costanti in fase di compilazione.

In generale, l'estensione non deve fare riferimento alle proprietà di configurazione costanti in fase di compilazione in fase di esecuzione.

È possibile identificare facilmente le proprietà di configurazione costanti in fase di compilazione perché la relativa definizione ha l'attributo CompileTimeEvaluation .

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

In rari casi, le proprietà di configurazione possono essere facoltative. In alcuni casi, potrebbe essere necessario implementare più proprietà di configurazione nella stessa classe. Ciò è comune quando si estendono ExtensionPart e implementano più interfacce, ognuna delle quali richiede la propria proprietà di configurazione.

Proprietà di configurazione autonome

Come descritto in precedenza, le classi di contributo di Visual Studio definiscono una classe singleton che in genere espone una o più proprietà di configurazione costanti in fase di compilazione. I valori delle proprietà di configurazione vengono salvati come metadati dell'estensione.

Alcune funzionalità di estendibilità richiedono di specificare i metadati dell'estensione che non sono associati ad alcuna classe ed è significativo da solo o che deve essere fatto riferimento da altre configurazioni. Alcuni esempi sono le definizioni di menu, barra degli strumenti e tipo di documento. Questa operazione viene ottenuta applicando l'attributo VisualStudioContribution a una proprietà di configurazione di sola lettura statica.

Le proprietà dei contributi di Visual Studio possono essere inserite in qualsiasi classe.

L'esempio Command Parenting definisce una barra degli strumenti dichiarando una proprietà statica di tipo ToolbarConfiguration e contrassegnandola come VisualStudioContribution.

namespace CommandParentingSample;

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

Le proprietà dei contributi di Visual Studio sono anche costanti in fase di compilazione e sono soggette alle stesse limitazioni descritte in precedenza.

Una proprietà di contributo di Visual Studio può anche fare riferimento a un'altra proprietà di configurazione. Ad esempio:

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>(),
        },
    };
    ...

I tipi destinati a essere usati per definire le proprietà dei contributi di Visual Studio implementano l'interfaccia IVisualStudioContributionProperty e sono contrassegnati con l'attributo CompileTimeEvaluation per documentare che i relativi valori vengono valutati quando viene compilata l'estensione.

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

Le linee guida per non fare riferimento alle proprietà di configurazione costanti in fase di compilazione in fase di esecuzione si applicano anche alle proprietà dei contributi di Visual Studio.

Se per una proprietà di contributo di Visual Studio è necessario un identificatore univoco, il nome completo (contenente il nome completo del tipo e il nome della proprietà) viene usato dall'infrastruttura VisualStudio.Extensibility come identificatore. Ad esempio, l'identificatore univoco della configurazione della barra degli strumenti descritta qui sarà CommandParentingSample.ExtensionCommandConfiguration.ToolbarConfiguration.