Okna narzędzi

Utwórz okno narzędzi z możliwością dokowania przy użyciu interfejsu użytkownika powiązanego z danymi przy użyciu interfejsu użytkownika zdalnego i zestawu VisualStudio.Extensibility SDK.

Note

W tym przewodniku użyto zestawu VisualStudio.Extensibility SDK. Zobacz Wybierz odpowiedni model rozszerzalności, aby porównać modele.

Pełny przykład

Kompletna implementacja:

Definicja okna narzędziowego:

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

Polecenie otwierające okno narzędzi:

// 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

Zobacz kompletny działający przykład: ToolWindowSample ↗

Powiązane pojęcia:Zdalny interfejs użytkownika — model powiązania danych używany na potrzeby zawartości okna narzędzi, Wkłady i konfiguracje — jak deklarowane są wkłady do okien narzędzi

Zrozum przykład

Krok 1: Zdefiniuj klasę okna narzędzia

Rozszerz ToolWindow i oznacz go za pomocą polecenia [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!));
    }
}

Krok 2: Utwórz model danych

Użyj NotifyPropertyChangedObject z [DataMember] i [DataContract] do powiązania danych zdalnego interfejsu użytkownika:

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

Krok 3: Tworzenie kontrolki Remote UI

Dodaj klasę RemoteUserControl i pasujący szablon danych XAML.

MyToolWindowControl.cs:

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

MyToolWindowControl.xaml — ustaw akcję kompilacji na EmbeddedResource (a nie 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

Plik XAML musi używać akcji kompilacji EmbeddedResource . Ustawienie tego na Page spowoduje, że okno narzędzia będzie renderowane jako puste.

Krok 4. Dodaj polecenie, aby wyświetlić okno narzędzia

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

Krok 5 (opcjonalnie): Dodać pasek narzędzi

Zdefiniuj pasek narzędzi i odwołaj się do niego w 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),
};

Porady

✅ DO

  • Oznacz pliki XAML jako akcję kompilacji EmbeddedResource (a nie stronę).
  • Użyj [DataContract] i [DataMember] na właściwościach ViewModel dla zdalnego interfejsu użytkownika.
  • Użyj NotifyPropertyChangedObject jako podstawy dla modeli danych.
  • Zainicjuj dane w pliku InitializeAsync(), utwórz interfejs użytkownika w pliku GetContentAsync().
  • Użyj ShowToolWindowAsync<T>(activate: true), aby wyświetlić dane wyjściowe poleceń.

❌ NIE RÓB

  • Nie używaj WPF bezpośrednio — w przypadku rozszerzeń poza procesem wymagany jest zdalny interfejs użytkownika.
  • Nie ustawiaj akcji kompilacji XAML na Page wartość (okno narzędzia nie będzie renderowane).
  • Nie twórz kosztownych zasobów w konstruktorze — użyj InitializeAsync.

🔧 Rozwiązywanie problemów

Objaw Napraw.
Okno narzędzia jest puste Sprawdź, czy akcja kompilacji XAML jest EmbeddedResource
Powiązanie danych nie jest aktualizowane Upewnij się, że model ViewModel dziedziczy po NotifyPropertyChangedObject i właściwości mają [DataMember]
Okno się nie pojawia Sprawdź, czy wywołania polecenia ShowToolWindowAsync są poprawne
Pasek narzędzi nie jest wyświetlany Sprawdź, czy ToolWindowConfiguration.Toolbar ustawiono

Następne kroki