Ventanas de herramientas

Cree una ventana de herramientas acoplable con la interfaz de usuario enlazada a datos mediante la interfaz de usuario remota y el SDK de extensibilidad de VisualStudio.Extensibility.

Note

Esta guía usa el SDK de VisualStudio.Extensibility. Consulte Elección del modelo de extensibilidad adecuado para comparar los modelos.

Ejemplo completo

La implementación completa:

Definición de la ventana de herramientas:

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace ToolWindowSample;

using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Commands;
using Microsoft.VisualStudio.Extensibility.ToolWindows;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;

/// <summary>
/// A sample tool window.
/// </summary>
[VisualStudioContribution]
public class MyToolWindow : ToolWindow
{
    private object? dataContext;

    /// <summary>
    /// Initializes a new instance of the <see cref="MyToolWindow" /> class.
    /// </summary>
    public MyToolWindow()
    {
        this.Title = "My Tool Window";
    }

    /// <inheritdoc />
    public override ToolWindowConfiguration ToolWindowConfiguration => new()
    {
        Placement = ToolWindowPlacement.DocumentWell,
        Toolbar = new ToolWindowToolbar(Toolbar),
    };

    [VisualStudioContribution]
    private static ToolbarConfiguration Toolbar => new("%ToolWindowSample.MyToolWindow.Toolbar.DisplayName%")
    {
        Children = [ToolbarChild.Command<MyToolbarCommand>()],
    };

    /// <inheritdoc />
    public override Task InitializeAsync(CancellationToken cancellationToken)
    {
        this.dataContext = new MyToolWindowData(this.Extensibility);

        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public override Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult<IRemoteUserControl>(new MyToolWindowControl(this.dataContext));
    }
}

Comando para abrir la ventana de herramientas:

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace ToolWindowSample;

using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Commands;

/// <summary>
/// A sample command for showing a tool window.
/// </summary>
[VisualStudioContribution]
public class MyToolWindowCommand : Command
{
    /// <inheritdoc />
    public override CommandConfiguration CommandConfiguration => new("%ToolWindowSample.MyToolWindowCommand.DisplayName%")
    {
        Placements = [CommandPlacement.KnownPlacements.ToolsMenu],
        Icon = new(ImageMoniker.KnownValues.ToolWindow, IconSettings.IconAndText),
    };

    /// <inheritdoc />
    public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
    {
        await this.Extensibility.Shell().ShowToolWindowAsync<MyToolWindow>(activate: true, cancellationToken);
    }
}

Tip

Consulte el ejemplo de trabajo completo: ToolWindowSample ↗

Conceptos relacionados:Interfaz de usuario remota: el modelo de enlace de datos que se usa para el contenido de la ventana de herramientas, las contribuciones y las configuraciones: cómo se declaran las contribuciones de la ventana de herramientas

Entiende el ejemplo

Paso 1: Definir la clase de ventana de herramientas

Extiéndalo ToolWindow y márquelo con [VisualStudioContribution]:

[VisualStudioContribution]
internal class MyToolWindow : ToolWindow
{
    private MyDataModel? dataModel;

    public MyToolWindow(VisualStudioExtensibility extensibility)
        : base(extensibility)
    {
        Title = "My Tool Window";
    }

    public override ToolWindowConfiguration ToolWindowConfiguration => new()
    {
        Placement = ToolWindowPlacement.DocumentWell,
        AllowAutoCreation = true,
    };

    public override async Task InitializeAsync(CancellationToken cancellationToken)
    {
        dataModel = new MyDataModel();
        await dataModel.LoadAsync(cancellationToken);
    }

    public override Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
    {
        return Task.FromResult<IRemoteUserControl>(new MyToolWindowControl(dataModel!));
    }
}

Paso 2: Crear el modelo de datos

Usa NotifyPropertyChangedObject con [DataContract] y [DataMember] para el enlace de datos de la interfaz de usuario remota:

[DataContract]
internal class MyDataModel : NotifyPropertyChangedObject
{
    private string message = "Hello from the tool window!";

    [DataMember]
    public string Message
    {
        get => message;
        set => SetProperty(ref message, value);
    }

    public async Task LoadAsync(CancellationToken cancellationToken)
    {
        // Load data asynchronously
    }
}

Paso 3: Crear el control de interfaz de usuario remota

Agregue una RemoteUserControl clase y una plantilla de datos XAML coincidente.

MyToolWindowControl.cs:

internal class MyToolWindowControl : RemoteUserControl
{
    public MyToolWindowControl(MyDataModel dataModel)
        : base(dataContext: dataModel)
    {
    }
}

MyToolWindowControl.xaml : establezca la acción de compilación en EmbeddedResource (no page):

<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml">
    <StackPanel Margin="10">
        <TextBlock Text="{Binding Message}" FontSize="14" />
    </StackPanel>
</DataTemplate>

Important

El archivo XAML debe usar la acción de EmbeddedResource compilación. Si se establece en Page, la ventana de herramientas se mostrará vacía.

Paso 4: Agregar un comando para mostrar la ventana de herramientas

[VisualStudioContribution]
internal class ShowMyToolWindowCommand : Command
{
    public ShowMyToolWindowCommand(VisualStudioExtensibility extensibility)
        : base(extensibility)
    {
    }

    public override CommandConfiguration CommandConfiguration => new("My Tool Window")
    {
        Placements = [CommandPlacement.KnownPlacements.ViewOtherWindowsMenu],
        Icon = new(ImageMoniker.KnownValues.ToolWindow, IconSettings.IconAndText),
    };

    public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
    {
        await Extensibility.Shell().ShowToolWindowAsync<MyToolWindow>(activate: true, cancellationToken);
    }
}

Paso 5 (opcional): Agregar una barra de herramientas

Defina una barra de herramientas y haga referencia a ella en ToolWindowConfiguration:

[VisualStudioContribution]
public static ToolbarConfiguration MyToolbar => new("%MyToolbar.DisplayName%")
{
    Children = [
        ToolbarChild.Command<RefreshCommand>(),
        ToolbarChild.Separator,
        ToolbarChild.Command<ClearCommand>(),
    ],
};

public override ToolWindowConfiguration ToolWindowConfiguration => new()
{
    Placement = ToolWindowPlacement.DocumentWell,
    Toolbar = new(MyToolbar),
};

Tips

✅ HACER

  • Marque los archivos XAML como acción de compilación EmbeddedResource (no Page).
  • Usa [DataContract] y [DataMember] en las propiedades de ViewModel para la interfaz de usuario remota.
  • Use NotifyPropertyChangedObject como base para los modelos de datos.
  • Inicialice los datos en InitializeAsync(), cree la interfaz de usuario en GetContentAsync().
  • Use ShowToolWindowAsync<T>(activate: true) para mostrar desde los comandos.

❌ INCORRECTO

  • No utilice WPF directamente: se requiere la interfaz de usuario remota para las extensiones fuera de proceso.
  • No establezca la acción de compilación del XAML en Page (la ventana de herramientas no se mostrará).
  • No cree recursos costosos en el constructor: use InitializeAsync.

🔧 Solución de problemas

Síntoma Corregir
La ventana de herramientas está vacía Comprobar que la acción de compilación xaml es EmbeddedResource
El enlace de datos no se actualiza Asegúrese de que ViewModel hereda NotifyPropertyChangedObject y las propiedades tienen [DataMember]
La ventana no aparece Compruebe que el comando llama correctamente a ShowToolWindowAsync
La barra de herramientas no se muestra Verifique que ToolWindowConfiguration.Toolbar esté establecido

Pasos siguientes