This commit is contained in:
Oleg Tkachenko 2022-08-01 15:03:53 -07:00
Родитель 3ab1a3f91a 7e8d708ac6
Коммит b92ce00ce5
29 изменённых файлов: 1682 добавлений и 76 удалений

Просмотреть файл

@ -66,8 +66,10 @@ namespace Microsoft.VisualStudio.Gladstone.DocumentSelectorSample
private async Task WriteToOutputWindowAsync(string message, CancellationToken cancellationToken)
{
var channel = await this.Extensibility.Views().Output.GetChannelAsync(nameof(DocumentSelectorSample),
nameof(Resources.OutputWindowPaneName), cancellationToken);
var channel = await this.Extensibility.Views().Output.GetChannelAsync(
identifier: nameof(DocumentSelectorSample),
displayNameResourceId: nameof(Resources.OutputWindowPaneName),
cancellationToken);
Assumes.NotNull(channel);
await channel.Writer.WriteLineAsync(message);
}

Просмотреть файл

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<NoWarn>$(NoWarn);CS1591;IDE0008;CA1812</NoWarn>
<NoWarn>$(NoWarn);CS1591;IDE0008;CA1812;CA2007</NoWarn>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<CodeAnalysisRuleSet>$(RepoRootPath)src\tests.ruleset</CodeAnalysisRuleSet>
<RootNamespace>Microsoft.VisualStudio.Gladstone.DocumentSelectorSample</RootNamespace>

Просмотреть файл

@ -9,8 +9,8 @@ Samples include:
* CommandRegistrationsSample: Showcases how to register commands in different ways and command placement, localization.
* InsertGuidExtension: A simple extension showing how to read/modify editor buffers.
* MarkdownLinter: An end-to-end example showing various features available in the extensibility SDK.
* CommentRemover: A conversion of Mads Kristensen's [Comment Remover](https://github.com/madskristensen/CommentRemover) extension to in-proc VisualStudio.Extensibility. This sample shows how to consume [Visual Studio SDK](https://www.nuget.org/packages/Microsoft.VisualStudio.SDK) services through .NET dependency injection and use VisualStudio.Extensibility APIs for commands, prompts and progress report.
* DocumentSelectorSample: A sample demonstrating how to define an editor extension that is only applicable to files matching a file path pattern.
* CommentRemover: A conversion of Mads Kristensen's [Comment Remover](https://github.com/madskristensen/CommentRemover) extension to in-proc VisualStudio.Extensibility. This sample shows how to consume [Visual Studio SDK](https://www.nuget.org/packages/Microsoft.VisualStudio.SDK) services through .NET dependency injection and use VisualStudio.Extensibility APIs for commands, prompts and progress report.
## Contributing

Просмотреть файл

@ -3,21 +3,25 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31516.38
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleRemoteCommandSample", "SimpleRemoteCommandSample\SimpleRemoteCommandSample.csproj", "{11EB62B3-79FD-450D-BC86-6FF414178B7E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{070F0AEA-C0A0-4B5D-9286-55574A37BE7A}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
shipping.ruleset = shipping.ruleset
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MarkdownLinter", "MarkdownLinter\MarkdownLinter.csproj", "{F005B03E-66DC-4145-8BBA-9E21ABE582C2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommandRegistrationsSample", "CommandRegistrationsSample\CommandRegistrationsSample.csproj", "{26601396-0E9A-4B38-8FE9-D5107B77D445}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InsertGuidExtension", "InsertGuidExtension\InsertGuidExtension.csproj", "{0B822716-63CA-41A9-8914-254A34C1976B}"
EndProject
<<<<<<< HEAD
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocumentSelectorSample", "DocumentSelectorSample\DocumentSelectorSample.csproj", "{6D16A4D7-4CF1-4162-9538-06F2D8499821}"
=======
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MarkdownLinter", "MarkdownLinter\MarkdownLinter.csproj", "{F005B03E-66DC-4145-8BBA-9E21ABE582C2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleRemoteCommandSample", "SimpleRemoteCommandSample\SimpleRemoteCommandSample.csproj", "{11EB62B3-79FD-450D-BC86-6FF414178B7E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ToolWindowExtension", "ToolWindowExtension\ToolWindowExtension.csproj", "{4605F69C-8179-409E-A3FA-431DD41F35CB}"
>>>>>>> 7e8d708ac6cc8b9d8c45aa5946ee531a0761e609
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -41,10 +45,17 @@ Global
{0B822716-63CA-41A9-8914-254A34C1976B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0B822716-63CA-41A9-8914-254A34C1976B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0B822716-63CA-41A9-8914-254A34C1976B}.Release|Any CPU.Build.0 = Release|Any CPU
<<<<<<< HEAD
{6D16A4D7-4CF1-4162-9538-06F2D8499821}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D16A4D7-4CF1-4162-9538-06F2D8499821}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D16A4D7-4CF1-4162-9538-06F2D8499821}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D16A4D7-4CF1-4162-9538-06F2D8499821}.Release|Any CPU.Build.0 = Release|Any CPU
=======
{4605F69C-8179-409E-A3FA-431DD41F35CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4605F69C-8179-409E-A3FA-431DD41F35CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4605F69C-8179-409E-A3FA-431DD41F35CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4605F69C-8179-409E-A3FA-431DD41F35CB}.Release|Any CPU.Build.0 = Release|Any CPU
>>>>>>> 7e8d708ac6cc8b9d8c45aa5946ee531a0761e609
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Просмотреть файл

@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace ToolWindowExtension
{
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.ToolWindows;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;
/// <summary>
/// A sample tool window.
/// </summary>
[ToolWindow(ToolWindowPlacement.DocumentWell)]
public class MyToolWindow : ToolWindow
{
private object? dataContext;
/// <summary>
/// Initializes a new instance of the <see cref="MyToolWindow" /> class.
/// </summary>
/// <param name="extensibility">
/// Extensibility object instance.
/// </param>
public MyToolWindow(VisualStudioExtensibility extensibility)
: base(extensibility)
{
this.Title = "My Tool Window";
}
/// <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));
}
}
}

Просмотреть файл

@ -0,0 +1,37 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace ToolWindowExtension
{
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Commands;
using Microsoft.VisualStudio.Extensibility.Definitions;
/// <summary>
/// A sample command for showing a tool window.
/// </summary>
[Command("ToolWindowExtension.MyToolWindowCommand", "My Tool Window", placement: CommandPlacement.ToolsMenu)]
[CommandIcon(KnownMonikers.ToolWindow, IconSettings.IconAndText)]
public class MyToolWindowCommand : Command
{
/// <summary>
/// Initializes a new instance of the <see cref="MyToolWindowCommand" /> class.
/// </summary>
/// <param name="extensibility">
/// Extensibility object instance.
/// </param>
/// <param name="name">
/// Command identifier.
/// </param>
public MyToolWindowCommand(VisualStudioExtensibility extensibility, string name)
: base(extensibility, name)
{
}
/// <inheritdoc />
public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
await this.Extensibility.Shell().ShowToolWindowAsync<MyToolWindow>(activate: true, cancellationToken);
}
}
}

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace ToolWindowExtension
{
using Microsoft.VisualStudio.Extensibility.UI;
/// <summary>
/// A sample remote user control to use as tool window UI content.
/// </summary>
internal class MyToolWindowControl : RemoteUserControl
{
/// <summary>
/// Initializes a new instance of the <see cref="MyToolWindowControl" /> class.
/// </summary>
/// <param name="dataContext">
/// Data context of the remote control which can be referenced from xaml through data binding.
/// </param>
/// <param name="synchronizationContext">
/// Optional synchronizationContext that the extender can provide to ensure that <see cref="IAsyncCommand"/>
/// are executed and properties are read and updated from the extension main thread.
/// </param>
public MyToolWindowControl(object? dataContext, SynchronizationContext? synchronizationContext = null)
: base(dataContext, synchronizationContext)
{
}
}
}

Просмотреть файл

@ -0,0 +1,36 @@
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:colors="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:styles="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0">
<Border Background="{DynamicResource {x:Static colors:EnvironmentColors.ToolWindowBackgroundBrushKey}}"
TextElement.Foreground="{DynamicResource {x:Static colors:EnvironmentColors.ToolWindowTextBrushKey}}"
Padding="5">
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Margin="0,0,5,0"
Text="Custom Message:"
VerticalAlignment="Center" />
<TextBox x:Name="TitleTextBox"
MinHeight="26"
MinWidth="200"
HorizontalContentAlignment="Left"
VerticalContentAlignment="Center"
Text="{Binding Message, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource {x:Static styles:VsResourceKeys.TextBoxStyleKey}}" />
<Button x:Name="ShowMessageButton"
Content="Show Dialog"
IsEnabled="False"
Margin="5,0,0,0"
MinHeight="25"
MinWidth="60"
Style="{StaticResource {x:Static styles:VsResourceKeys.ButtonStyleKey}}"
Command="{Binding ShowMessageCommand}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding HasError}" Value="False">
<Setter TargetName="ShowMessageButton" Property="IsEnabled" Value="True" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

Просмотреть файл

@ -0,0 +1,86 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace ToolWindowExtension
{
using System.Runtime.Serialization;
using Microsoft;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Shell;
using Microsoft.VisualStudio.Extensibility.UI;
/// <summary>
/// A sample data context object to use with tool window UI content.
/// </summary>
[DataContract]
internal class MyToolWindowData : NotifyPropertyChangedObject
{
private bool hasError;
private string message = "My custom message";
/// <summary>
/// Initializes a new instance of the <see cref="MyToolWindowData" /> class.
/// </summary>
/// <param name="extensibility">
/// Extensibility object instance.
/// </param>
public MyToolWindowData(VisualStudioExtensibility extensibility)
{
Requires.NotNull(extensibility, nameof(extensibility));
this.Context = new ClientContext(new Dictionary<string, object?>(), extensibility);
this.ShowMessageCommand = new AsyncCommand(this.ShowMessageAsync);
}
/// <summary>
/// Gets the async command used to show a message prompt.
/// </summary>
[DataMember]
public IAsyncCommand ShowMessageCommand
{
get;
}
/// <summary>
/// Gets or sets a value indicating whether there is an error in the data context.
/// </summary>
[DataMember]
public bool HasError
{
get => this.hasError;
set => this.SetProperty(ref this.hasError, value);
}
/// <summary>
/// Gets or sets the message to display in the message prompt.
/// </summary>
[DataMember]
public string Message
{
get => this.message;
set
{
if (string.IsNullOrWhiteSpace(value))
{
this.HasError = true;
}
else
{
this.HasError = false;
}
this.SetProperty(ref this.message, value);
}
}
private ClientContext Context
{
get;
}
private async Task ShowMessageAsync(object? commandParameter, CancellationToken cancellationToken)
{
await this.Context.ShowPromptAsync(this.Message, PromptOptions.OK, cancellationToken);
}
}
}

Просмотреть файл

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>10</LangVersion>
<Nullable>enable</Nullable>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.4.11-alpha" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MyToolWindowControl.xaml" />
</ItemGroup>
<ItemGroup>
<Page Remove="MyToolWindowControl.xaml" />
</ItemGroup>
</Project>

Просмотреть файл

@ -26,6 +26,9 @@ For future updates please bookmark our [announcements](docs/announcements.md) pa
* [Commands](docs/new-extensibility-model/extension-guides/command/command.md)
* [Editor components](docs/new-extensibility-model/extension-guides/editor/editor.md)
* [Rule based conditions](docs/new-extensibility-model/inside-the-sdk/activation-constraints.md)
* [Remote UI](docs/new-extensibility-model/inside-the-sdk/remote-ui.md)
* [Advanced Remote UI concepts](docs/new-extensibility-model/inside-the-sdk/advanced-remote-ui.md)
* [Tool Windows](docs/new-extensibility-model/extension-guides/toolWindow/toolWindow.md)
## Samples and walkthroughs
A Visual Studio solution containing all samples can be found at [Samples.sln](https://github.com/microsoft/VSExtensibility/tree/main/New_Extensibility_Model/Samples/Samples.sln).
@ -35,6 +38,7 @@ A Visual Studio solution containing all samples can be found at [Samples.sln](ht
* [Insert guid extension sample](https://github.com/microsoft/VSExtensibility/tree/main/New_Extensibility_Model/Samples/InsertGuidExtension)
* [Command registration, localization sample](https://github.com/microsoft/VSExtensibility/tree/main/New_Extensibility_Model/Samples/CommandRegistrationsSample)
* [Comment Remover, in-proc extension sample](https://github.com/microsoft/VSExtensibility/tree/main/New_Extensibility_Model/Samples/CommentRemover)
* [Tool Window sample](./New_Extensibility_Model/Samples/ToolWindowExtension/)
## API Docs

Просмотреть файл

@ -73,6 +73,12 @@
- [ClearDiagnosticsAsync(textDocument,cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-Documents-DiagnosticsProvider-ClearDiagnosticsAsync-Microsoft-VisualStudio-Extensibility-Editor-Data-ITextDocument,System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.Documents.DiagnosticsProvider.ClearDiagnosticsAsync(Microsoft.VisualStudio.Extensibility.Editor.Data.ITextDocument,System.Threading.CancellationToken)')
- [ClearDiagnosticsAsync(documentMoniker,cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-Documents-DiagnosticsProvider-ClearDiagnosticsAsync-System-Uri,System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.Documents.DiagnosticsProvider.ClearDiagnosticsAsync(System.Uri,System.Threading.CancellationToken)')
- [Dispose()](#M-Microsoft-VisualStudio-Extensibility-Documents-DiagnosticsProvider-Dispose-System-Boolean- 'Microsoft.VisualStudio.Extensibility.Documents.DiagnosticsProvider.Dispose(System.Boolean)')
- [Dock](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock')
- [Bottom](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Bottom 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock.Bottom')
- [Left](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Left 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock.Left')
- [None](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-None 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock.None')
- [Right](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Right 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock.Right')
- [Top](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Top 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock.Top')
- [ExecutableCommand](#T-Microsoft-VisualStudio-Extensibility-Commands-ExecutableCommand 'Microsoft.VisualStudio.Extensibility.Commands.ExecutableCommand')
- [#ctor(id)](#M-Microsoft-VisualStudio-Extensibility-Commands-ExecutableCommand-#ctor-System-UInt16- 'Microsoft.VisualStudio.Extensibility.Commands.ExecutableCommand.#ctor(System.UInt16)')
- [Id](#P-Microsoft-VisualStudio-Extensibility-Commands-ExecutableCommand-Id 'Microsoft.VisualStudio.Extensibility.Commands.ExecutableCommand.Id')
@ -148,6 +154,13 @@
- [InvalidVersionString](#P-Microsoft-VisualStudio-Extensibility-Resources-InvalidVersionString 'Microsoft.VisualStudio.Extensibility.Resources.InvalidVersionString')
- [ResourceManager](#P-Microsoft-VisualStudio-Extensibility-Resources-ResourceManager 'Microsoft.VisualStudio.Extensibility.Resources.ResourceManager')
- [UnsupportedDocumentMoniker](#P-Microsoft-VisualStudio-Extensibility-Resources-UnsupportedDocumentMoniker 'Microsoft.VisualStudio.Extensibility.Resources.UnsupportedDocumentMoniker')
- [ShellExtensibility](#T-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility 'Microsoft.VisualStudio.Extensibility.Shell.ShellExtensibility')
- [#ctor(extensibilityPoint)](#M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-#ctor-Microsoft-VisualStudio-Extensibility-ExtensibilityPoint- 'Microsoft.VisualStudio.Extensibility.Shell.ShellExtensibility.#ctor(Microsoft.VisualStudio.Extensibility.ExtensibilityPoint)')
- [Dispose()](#M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-Dispose-System-Boolean- 'Microsoft.VisualStudio.Extensibility.Shell.ShellExtensibility.Dispose(System.Boolean)')
- [GetToolWindow\`\`1()](#M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-GetToolWindow``1 'Microsoft.VisualStudio.Extensibility.Shell.ShellExtensibility.GetToolWindow``1')
- [HideToolWindowAsync\`\`1(cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-HideToolWindowAsync``1-System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.Shell.ShellExtensibility.HideToolWindowAsync``1(System.Threading.CancellationToken)')
- [ServiceAvailabilityChanged(sender,args)](#M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-ServiceAvailabilityChanged-System-Object,Microsoft-ServiceHub-Framework-BrokeredServicesChangedEventArgs- 'Microsoft.VisualStudio.Extensibility.Shell.ShellExtensibility.ServiceAvailabilityChanged(System.Object,Microsoft.ServiceHub.Framework.BrokeredServicesChangedEventArgs)')
- [ShowToolWindowAsync\`\`1(activate,cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-ShowToolWindowAsync``1-System-Boolean,System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.Shell.ShellExtensibility.ShowToolWindowAsync``1(System.Boolean,System.Threading.CancellationToken)')
- [SynchronizedBufferTextWriter](#T-Microsoft-VisualStudio-Extensibility-Documents-SynchronizedBufferTextWriter 'Microsoft.VisualStudio.Extensibility.Documents.SynchronizedBufferTextWriter')
- [#ctor(pipeWriter,encoding)](#M-Microsoft-VisualStudio-Extensibility-Documents-SynchronizedBufferTextWriter-#ctor-System-IO-Pipelines-PipeWriter,System-Text-Encoding- 'Microsoft.VisualStudio.Extensibility.Documents.SynchronizedBufferTextWriter.#ctor(System.IO.Pipelines.PipeWriter,System.Text.Encoding)')
- [Dispose()](#M-Microsoft-VisualStudio-Extensibility-Documents-SynchronizedBufferTextWriter-Dispose-System-Boolean- 'Microsoft.VisualStudio.Extensibility.Documents.SynchronizedBufferTextWriter.Dispose(System.Boolean)')
@ -156,6 +169,29 @@
- [ToggleCommand](#T-Microsoft-VisualStudio-Extensibility-Commands-ToggleCommand 'Microsoft.VisualStudio.Extensibility.Commands.ToggleCommand')
- [#ctor(extensibility,id)](#M-Microsoft-VisualStudio-Extensibility-Commands-ToggleCommand-#ctor-Microsoft-VisualStudio-Extensibility-VisualStudioExtensibility,System-UInt16- 'Microsoft.VisualStudio.Extensibility.Commands.ToggleCommand.#ctor(Microsoft.VisualStudio.Extensibility.VisualStudioExtensibility,System.UInt16)')
- [IsChecked](#P-Microsoft-VisualStudio-Extensibility-Commands-ToggleCommand-IsChecked 'Microsoft.VisualStudio.Extensibility.Commands.ToggleCommand.IsChecked')
- [ToolWindow](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow')
- [#ctor(extensibility)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-#ctor-Microsoft-VisualStudio-Extensibility-VisualStudioExtensibility- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.#ctor(Microsoft.VisualStudio.Extensibility.VisualStudioExtensibility)')
- [Extensibility](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Extensibility 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.Extensibility')
- [Title](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Title 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.Title')
- [Dispose()](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Dispose 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.Dispose')
- [Dispose(disposing)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Dispose-System-Boolean- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.Dispose(System.Boolean)')
- [GetContentAsync(cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-GetContentAsync-System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.GetContentAsync(System.Threading.CancellationToken)')
- [HideAsync(cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-HideAsync-System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.HideAsync(System.Threading.CancellationToken)')
- [InitializeAsync(cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-InitializeAsync-System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.InitializeAsync(System.Threading.CancellationToken)')
- [OnHideAsync(cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-OnHideAsync-System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.OnHideAsync(System.Threading.CancellationToken)')
- [OnShowAsync(cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-OnShowAsync-System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.OnShowAsync(System.Threading.CancellationToken)')
- [ShowAsync(activate,cancellationToken)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-ShowAsync-System-Boolean,System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.ShowAsync(System.Boolean,System.Threading.CancellationToken)')
- [ToolWindowAttribute](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute')
- [#ctor(placement,dockDirection,allowAutoCreation)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-#ctor-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement,Microsoft-VisualStudio-Extensibility-ToolWindows-Dock,System-Boolean- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.#ctor(Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement,Microsoft.VisualStudio.Extensibility.ToolWindows.Dock,System.Boolean)')
- [#ctor(placement,dockDirection,allowAutoCreation)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-#ctor-System-String,Microsoft-VisualStudio-Extensibility-ToolWindows-Dock,System-Boolean- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.#ctor(System.String,Microsoft.VisualStudio.Extensibility.ToolWindows.Dock,System.Boolean)')
- [AllowAutoCreation](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-AllowAutoCreation 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.AllowAutoCreation')
- [DockDirection](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-DockDirection 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.DockDirection')
- [Placement](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-Placement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.Placement')
- [ToolWindowPlacement](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement')
- [DocumentWell](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement-DocumentWell 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement.DocumentWell')
- [Floating](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement-Floating 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement.Floating')
- [ToolWindowVisibleWhenAttribute](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowVisibleWhenAttribute 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowVisibleWhenAttribute')
- [#ctor(expression,termNames,termValues)](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowVisibleWhenAttribute-#ctor-System-String,System-String[],System-String[]- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowVisibleWhenAttribute.#ctor(System.String,System.String[],System.String[])')
- [ViewsExtensibility](#T-Microsoft-VisualStudio-Extensibility-Documents-ViewsExtensibility 'Microsoft.VisualStudio.Extensibility.Documents.ViewsExtensibility')
- [#ctor(extensibilityPoint)](#M-Microsoft-VisualStudio-Extensibility-Documents-ViewsExtensibility-#ctor-Microsoft-VisualStudio-Extensibility-ExtensibilityPoint- 'Microsoft.VisualStudio.Extensibility.Documents.ViewsExtensibility.#ctor(Microsoft.VisualStudio.Extensibility.ExtensibilityPoint)')
- [Output](#P-Microsoft-VisualStudio-Extensibility-Documents-ViewsExtensibility-Output 'Microsoft.VisualStudio.Extensibility.Documents.ViewsExtensibility.Output')
@ -1073,6 +1109,52 @@ A [Task](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=
This method has no parameters.
<a name='T-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock'></a>
## Dock `type`
##### Namespace
Microsoft.VisualStudio.Extensibility.ToolWindows
##### Summary
Dock direction that can be used to control how a tool window is docked when it's first shown.
<a name='F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Bottom'></a>
### Bottom `constants`
##### Summary
Docks the tool window below its placement target.
<a name='F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Left'></a>
### Left `constants`
##### Summary
Docks the tool window to the left of its placement target.
<a name='F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-None'></a>
### None `constants`
##### Summary
Docks the tool window alongside its placement target.
<a name='F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Right'></a>
### Right `constants`
##### Summary
Docks the tool window to the right of its placement target.
<a name='F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Top'></a>
### Top `constants`
##### Summary
Docks the tool window above its placement target.
<a name='T-Microsoft-VisualStudio-Extensibility-Commands-ExecutableCommand'></a>
## ExecutableCommand `type`
@ -1921,6 +2003,123 @@ Returns the cached ResourceManager instance used by this class.
Looks up a localized string similar to Unsupported document moniker..
<a name='T-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility'></a>
## ShellExtensibility `type`
##### Namespace
Microsoft.VisualStudio.Extensibility.Shell
##### Summary
Provides functionality to interact with Visual Studio shell services such as progress reporting, notifications, etc.
<a name='M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-#ctor-Microsoft-VisualStudio-Extensibility-ExtensibilityPoint-'></a>
### #ctor(extensibilityPoint) `constructor`
##### Summary
Initializes a new instance of the [ShellExtensibility](#T-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility 'Microsoft.VisualStudio.Extensibility.Shell.ShellExtensibility') class.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| extensibilityPoint | [Microsoft.VisualStudio.Extensibility.ExtensibilityPoint](#T-Microsoft-VisualStudio-Extensibility-ExtensibilityPoint 'Microsoft.VisualStudio.Extensibility.ExtensibilityPoint') | Another instance of extensibility point object to use for initialization. |
<a name='M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-Dispose-System-Boolean-'></a>
### Dispose() `method`
##### Summary
*Inherit from parent.*
##### Parameters
This method has no parameters.
<a name='M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-GetToolWindow``1'></a>
### GetToolWindow\`\`1() `method`
##### Summary
Gets the tool window of the given type.
##### Returns
The tool window matching the given type or null if not found.
##### Parameters
This method has no parameters.
##### Generic Types
| Name | Description |
| ---- | ----------- |
| T | The type of the tool window. |
<a name='M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-HideToolWindowAsync``1-System-Threading-CancellationToken-'></a>
### HideToolWindowAsync\`\`1(cancellationToken) `method`
##### Summary
Hides the tool window of the given type.
##### Returns
A [Task](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.Tasks.Task 'System.Threading.Tasks.Task') representing the asynchronous operation.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A [CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') to cancel the in-progress request. |
##### Generic Types
| Name | Description |
| ---- | ----------- |
| T | The type of the tool window. |
<a name='M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-ServiceAvailabilityChanged-System-Object,Microsoft-ServiceHub-Framework-BrokeredServicesChangedEventArgs-'></a>
### ServiceAvailabilityChanged(sender,args) `method`
##### Summary
Handles the service instance when availability of brokered services has changed.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| sender | [System.Object](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Object 'System.Object') | Sender. |
| args | [Microsoft.ServiceHub.Framework.BrokeredServicesChangedEventArgs](#T-Microsoft-ServiceHub-Framework-BrokeredServicesChangedEventArgs 'Microsoft.ServiceHub.Framework.BrokeredServicesChangedEventArgs') | BrokeredServicesChangedEventArgs. |
<a name='M-Microsoft-VisualStudio-Extensibility-Shell-ShellExtensibility-ShowToolWindowAsync``1-System-Boolean,System-Threading-CancellationToken-'></a>
### ShowToolWindowAsync\`\`1(activate,cancellationToken) `method`
##### Summary
Shows the tool window of the given type.
##### Returns
A [Task](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.Tasks.Task 'System.Threading.Tasks.Task') representing the asynchronous operation.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| activate | [System.Boolean](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Boolean 'System.Boolean') | Whether or not to activate the tool window during the show. Activating a tool window will move focus into it. |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A [CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') to cancel the in-progress request. |
##### Generic Types
| Name | Description |
| ---- | ----------- |
| T | The type of the tool window. |
<a name='T-Microsoft-VisualStudio-Extensibility-Documents-SynchronizedBufferTextWriter'></a>
## SynchronizedBufferTextWriter `type`
@ -2011,6 +2210,314 @@ Initializes a new instance of the [ToggleCommand](#T-Microsoft-VisualStudio-Exte
*Inherit from parent.*
<a name='T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow'></a>
## ToolWindow `type`
##### Namespace
Microsoft.VisualStudio.Extensibility.ToolWindows
##### Summary
Provides functionality for defining and interacting with a tool window.
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-#ctor-Microsoft-VisualStudio-Extensibility-VisualStudioExtensibility-'></a>
### #ctor(extensibility) `constructor`
##### Summary
Initializes a new instance of the [ToolWindow](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow') class.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| extensibility | [Microsoft.VisualStudio.Extensibility.VisualStudioExtensibility](#T-Microsoft-VisualStudio-Extensibility-VisualStudioExtensibility 'Microsoft.VisualStudio.Extensibility.VisualStudioExtensibility') | Extensibility object. |
<a name='P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Extensibility'></a>
### Extensibility `property`
##### Summary
Gets the Visual Studio extensibility point.
<a name='P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Title'></a>
### Title `property`
##### Summary
Gets or sets the title of the tool window.
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Dispose'></a>
### Dispose() `method`
##### Summary
*Inherit from parent.*
##### Parameters
This method has no parameters.
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Dispose-System-Boolean-'></a>
### Dispose(disposing) `method`
##### Summary
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| disposing | [System.Boolean](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Boolean 'System.Boolean') | Whether managed resources should be disposed. This should be `false` when
[Dispose](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-Dispose-System-Boolean- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.Dispose(System.Boolean)') is called from a finalizer, `true` otherwise. |
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-GetContentAsync-System-Threading-CancellationToken-'></a>
### GetContentAsync(cancellationToken) `method`
##### Summary
Gets the tool window's UI content.
##### Returns
An [IRemoteUserControl](#T-Microsoft-VisualStudio-RpcContracts-RemoteUI-IRemoteUserControl 'Microsoft.VisualStudio.RpcContracts.RemoteUI.IRemoteUserControl') which represents the tool window's UI content.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token to cancel the in-progress content creation. |
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-HideAsync-System-Threading-CancellationToken-'></a>
### HideAsync(cancellationToken) `method`
##### Summary
Hides the tool window.
##### Returns
A [Task](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.Tasks.Task 'System.Threading.Tasks.Task') representing the asynchronous operation.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token to cancel the in-progress hide. |
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-InitializeAsync-System-Threading-CancellationToken-'></a>
### InitializeAsync(cancellationToken) `method`
##### Summary
Initializes the tool window.
##### Returns
A [Task](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.Tasks.Task 'System.Threading.Tasks.Task') representing the asynchronous operation.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token to cancel the in-progress initialization. |
##### Remarks
This method is called prior to calling [GetContentAsync](#M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-GetContentAsync-System-Threading-CancellationToken- 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow.GetContentAsync(System.Threading.CancellationToken)') and can be used
for any work that needs to be done prior to creating the tool window's UI content.
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-OnHideAsync-System-Threading-CancellationToken-'></a>
### OnHideAsync(cancellationToken) `method`
##### Summary
Notifies that the tool window is being hidden.
##### Returns
A [Task](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.Tasks.Task 'System.Threading.Tasks.Task') representing the asynchronous operation.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token to cancel the in-progress hide. |
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-OnShowAsync-System-Threading-CancellationToken-'></a>
### OnShowAsync(cancellationToken) `method`
##### Summary
Notifies that the tool window is being shown.
##### Returns
A [Task](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.Tasks.Task 'System.Threading.Tasks.Task') representing the asynchronous operation.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token to cancel the in-progress show. |
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindow-ShowAsync-System-Boolean,System-Threading-CancellationToken-'></a>
### ShowAsync(activate,cancellationToken) `method`
##### Summary
Shows the tool window.
##### Returns
A [Task](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.Tasks.Task 'System.Threading.Tasks.Task') representing the asynchronous operation.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| activate | [System.Boolean](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Boolean 'System.Boolean') | Whether or not to activate the tool window during the show. Activating a tool window will move focus into it. |
| cancellationToken | [System.Threading.CancellationToken](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Threading.CancellationToken 'System.Threading.CancellationToken') | A cancellation token to cancel the in-progress hide. |
<a name='T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute'></a>
## ToolWindowAttribute `type`
##### Namespace
Microsoft.VisualStudio.Extensibility.ToolWindows
##### Summary
An attribute used on ToolWindow classes to register a tool window and define its default behaviors.
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-#ctor-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement,Microsoft-VisualStudio-Extensibility-ToolWindows-Dock,System-Boolean-'></a>
### #ctor(placement,dockDirection,allowAutoCreation) `constructor`
##### Summary
Initializes a new instance of the [ToolWindowAttribute](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute') class.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| placement | [Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement') | The placement location for the tool window. Valid values come from [ToolWindowPlacement](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement'). |
| dockDirection | [Microsoft.VisualStudio.Extensibility.ToolWindows.Dock](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock') | The direction relative to the placement where the tool window should be docked. |
| allowAutoCreation | [System.Boolean](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Boolean 'System.Boolean') | Whether or not the tool window can be created automatically. |
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-#ctor-System-String,Microsoft-VisualStudio-Extensibility-ToolWindows-Dock,System-Boolean-'></a>
### #ctor(placement,dockDirection,allowAutoCreation) `constructor`
##### Summary
Initializes a new instance of the [ToolWindowAttribute](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute') class.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| placement | [System.String](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.String 'System.String') | The placement location for the tool window. Valid values come from guids of other tool windows. |
| dockDirection | [Microsoft.VisualStudio.Extensibility.ToolWindows.Dock](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock') | The direction relative to the placement where the tool window should be docked. |
| allowAutoCreation | [System.Boolean](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.Boolean 'System.Boolean') | Whether or not the tool window can be created automatically. |
<a name='P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-AllowAutoCreation'></a>
### AllowAutoCreation `property`
##### Summary
Gets a value indicating whether the tool window can be created automatically.
##### Remarks
When true, the tool window will be created if it should be visible in the current window layout or when any
relevant visibility contexts activate. When false the tool window will not be created as part of loading any
window layout or due to visibility contexts, and the only way to trigger the creation is to manually show it.
<a name='P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-DockDirection'></a>
### DockDirection `property`
##### Summary
Gets the dock direction where the tool window should be placed relative to the [Placement](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-Placement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.Placement') value.
##### Remarks
If the [Placement](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-Placement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.Placement') has the value [Floating](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement-Floating 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement.Floating') this value is ignored. If
the [Placement](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-Placement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.Placement') has the value [DocumentWell](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement-DocumentWell 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement.DocumentWell') this value will dock
the tool window to the corresponding side of the main window's document well. If the [Placement](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-Placement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.Placement') is
any other value the tool window will be docked relative to the [Placement](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-Placement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.Placement') based on the value of the
"Insert new tabs to the right" setting with the exception of [Left](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-Dock-Left 'Microsoft.VisualStudio.Extensibility.ToolWindows.Dock.Left') which will always dock to
the left of the [Placement](#P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-Placement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute.Placement').
<a name='P-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowAttribute-Placement'></a>
### Placement `property`
##### Summary
Gets the default placement location for the tool window. This can be a well-known string from
[ToolWindowPlacement](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement'), or the guid string of another tool window.
##### Remarks
If the value is [Floating](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement-Floating 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement.Floating') the tool window will be created in its own floating
window. If the value is [DocumentWell](#F-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement-DocumentWell 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowPlacement.DocumentWell') the tool window will be docked in
relation to the main window's document well. Any other value will attempt to find the referenced location and
dock the tool window relative to that location, but if that location cannot be found, the tool window will
fallback to opening in a floating window.
<a name='T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement'></a>
## ToolWindowPlacement `type`
##### Namespace
Microsoft.VisualStudio.Extensibility.ToolWindows
##### Summary
Known default tool window placements.
<a name='F-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement-DocumentWell'></a>
### DocumentWell `constants`
##### Summary
Places the tool window in the active document well by default.
<a name='F-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowPlacement-Floating'></a>
### Floating `constants`
##### Summary
Places the tool window in a standalone floating window by default.
<a name='T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowVisibleWhenAttribute'></a>
## ToolWindowVisibleWhenAttribute `type`
##### Namespace
Microsoft.VisualStudio.Extensibility.ToolWindows
##### Summary
An attribute used on ToolWindow classes to register a visibility context for showing a tool window.
<a name='M-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowVisibleWhenAttribute-#ctor-System-String,System-String[],System-String[]-'></a>
### #ctor(expression,termNames,termValues) `constructor`
##### Summary
Initializes a new instance of the [ToolWindowVisibleWhenAttribute](#T-Microsoft-VisualStudio-Extensibility-ToolWindows-ToolWindowVisibleWhenAttribute 'Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowVisibleWhenAttribute') class.
##### Parameters
| Name | Type | Description |
| ---- | ---- | ----------- |
| expression | [System.String](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.String 'System.String') | A boolean expression string. |
| termNames | [System.String[]](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.String[] 'System.String[]') | The names of the terms used in the expression. |
| termValues | [System.String[]](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.String[] 'System.String[]') | The values of each term in the same order as term names array. |
<a name='T-Microsoft-VisualStudio-Extensibility-Documents-ViewsExtensibility'></a>
## ViewsExtensibility `type`

Просмотреть файл

@ -81,6 +81,47 @@ new extensibility model.
ContentTypes are hierarchical. e.g.: C# and C++ both descend from "code", so declaring "code" will cause your extension
to activate for C#, C, C++, etc.
#### Definitng a new content type
[ContentTypeDefinition](./../../api/Microsoft.VisualStudio.Extensibility.Editor.md#T-Microsoft-VisualStudio-Extensibility-Editor-ContentTypeDefinition), [ContentTypeBaseDefinition](./../../api/Microsoft.VisualStudio.Extensibility.Editor.md#T-Microsoft-VisualStudio-Extensibility-Editor-ContentTypeBaseDefinition) and [FileExtensionMapping](./../../api/Microsoft.VisualStudio.Extensibility.Editor.md#T-Microsoft-VisualStudio-Extensibility-Editor-FileExtensionMapping) attributes allow defining a new content type, inheriting one or more other content types and map it to one or more file extensions. These are assembly level attributes:
```csharp
using Microsoft.VisualStudio.Extensibility.Editor;
[assembly: ContentTypeDefinition("markdown")]
[assembly: ContentTypeBaseDefinition("markdown", baseContentTypeName: "text")]
[assembly: FileExtensionMapping("markdown", fileExtension: ".md")]
[assembly: FileExtensionMapping("markdown", fileExtension: ".mdk")]
[assembly: FileExtensionMapping("markdown", fileExtension: ".markdown")]
```
Content type definitions are merged with content type definitions provided by legacy Visual Studio extensibility, which allows for example to map additional file extensions to existing content types.
#### Document Selectors
In addition to [AppliesTo](./../../api/Microsoft.VisualStudio.Extensibility.Editor.md#T-Microsoft-VisualStudio-Extensibility-Editor-AppliesToAttribute) attribute, [AppliesToPattern](./../../api/Microsoft.VisualStudio.Extensibility.Editor.md#T-Microsoft-VisualStudio-Extensibility-Editor-AppliesToPattern) attribute allows to further limit applicability of the extension by making it activate only when document's file path matches a glob pattern:
```csharp
[AppliesTo(ContentType = "CSharp")]
[AppliesToPattern(Pattern = "**/tests/*.cs")]
```
```csharp
[AppliesTo(ContentType = "markdown")]
[AppliesToPattern(Pattern="docs/*.md", RelativePath=true)]
```
The Pattern property represents a glob pattern that is matched on the absolute path of the document.
Glob patterns can have the following syntax:
* `*` to match zero or more characters in a path segment
* `?` to match on one character in a path segment
* `**` to match any number of path segments, including none
* `{}` to group conditions (e.g. `**/*.{ts,js}` matches all TypeScript and JavaScript files)
* `[]` to declare a range of characters to match in a path segment (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …)
* `[!...]` to negate a range of characters to match in a path segment (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but not `example.0`)
Note: a backslash (`\`) is not valid within a glob pattern. Make sure to convert any backslash to slash when creating the glob pattern.
### EditorExtensibility
Visual Studio ExtensionParts all expose a [this.Extensibility](./../../api/Microsoft.VisualStudio.Extensibility.Framework.md#P-Microsoft-VisualStudio-Extensibility-ExtensionPart-Extensibility) property. Using this property, you can

Просмотреть файл

@ -0,0 +1,152 @@
---
title: Tool Windows reference
description: A reference for extensibility tool windows
date: 2022-7-20
---
# Tool Windows
Tool windows are a way to add complex UI and interactions to Visual Studio. They typically provide a user friendly mechanism for interacting with various APIs and features. For example, the Solution Explorer provides a tree-based view of the current project/solution/folder and allows simple gestures for opening, renaming, creating files.
Tool windows are single-instance , meaning that only one instance of the tool window can be open at a time. After a single-instance tool window is opened, it remains open until Visual Studio is closed, and when closed the tool window is only visibly hidden instead of being fully closed and disposed like documents. Tool windows can be dynamic, meaning that they are visible whenever their related context rule applies. The use of auto-visibility can reduce the clutter of windows in the IDE. Tool windows can be docked, floating, or tabbed in the document well. The default size and location apply only when the tool window is first opened and after that the tool window state is persisted.
## Creating new tool windows
To get started, follow the [create the project](../getting-started/create-your-first-extension.md) section in Getting Started section.
### Registering a tool window
Creating a tool window with the new Extensibility Model is as simple as extending the base class `Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindow` and adorning your class with the attribute `Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute`.
The attribute [Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowAttribute](./../../api/Microsoft.VisualStudio.Extensibility.md/#toolwindowattribute-type) has a few parameters that you should become familiar with:
| Parameter | Type | Required | Description |
| --------- |----- | -------- | ----------- |
| Placement | ToolWindowPlacement or String | No | The location in Visual Studio where the tool window should be opened the first time. If this value is a string it's expected to be a guid matching an old vsix-style tool window id. See more about [ToolWindowPlacement](#tool-window-placement). |
| DockDirection | Dock | No | The direction relative to the placement where the tool window should be docked when opened the first time. See more about [Dock](#dock). |
| AllowAutoCreation | Bool | No | Whether or not the tool window can be created automatically. Setting this to false means that tool windows that are open when Visual Studio is closed will not be automatically restored when Visual Studio is opened again. |
```csharp
// This is a default tool window registration that will result in:
// 1) Placement of ToolWindowPlacement.Floating
// 2) Dock direction of Dock.None
// 3) Allow auto-creation being enabled
[ToolWindow]
public class MyToolWindow : ToolWindow
{
public MyToolWindow(VisualStudioExtensibility extensibility)
: base(extensibility)
{
this.Title = "My Tool Window";
}
public override Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
{
// TODO: Create and return a RemoteUserControl
}
}
```
See the [ToolWindowExtension](./../../../../New_Extensibility_Model/Samples/ToolWindowExtension) sample to get started with creating an extension with a tool window.
### Creating content for a tool window
Once a tool window is registered it will need content. Adding content requires creating a [RemoteUserControl](./../../inside-the-sdk/remote-ui.md) and corresponding data template for the control.
```csharp
[ToolWindow(ToolWindowPlacement.DocumentWell)]
public class MyToolWindow : ToolWindow
{
public MyToolWindow(VisualStudioExtensibility extensibility)
: base(extensibility)
{
this.Title = "My Tool Window";
}
public override Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
{
// The data object allows for data binding in the control.
var dataContext = new MyToolWindowData(this.Extensibility);
// The only work done here should be related to creating the content.
// If additional work is needed prior to creating the control, it
// can be done by overriding the InitializeAsync method
var control = new MyToolWindowControl(dataContext);
return Task.FromResult<IRemoteUserControl>(control);
}
}
```
MyToolWindowControl.cs: (this is an example file name and should have the same name as the data template file)
```csharp
internal class MyToolWindowControl : RemoteUserControl
{
public MyToolWindowControl(object? dataContext, SynchronizationContext? synchronizationContext = null)
: base(dataContext, synchronizationContext)
{
}
}
```
MyToolWindowControl.xaml (this is an example file name and should have the same name as the class that derives from RemoteUserControl)
```xml
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- TODO: Fill with content -->
</DataTemplate>
```
See the [Remote UI](./../../inside-the-sdk/remote-ui.md) docs for more information on creating a RemoteUserControl.
### Registering a command to show tool window
A common mechanism for opening a tool window is to add a command that, when invoked, shows the tool window.
```csharp
[Command("ToolWindowExtension.MyToolWindowCommand", "My Tool Window", placement: CommandPlacement.ToolsMenu)]
[CommandIcon(KnownMonikers.ToolWindow, IconSettings.IconAndText)]
public class MyToolWindowCommand : Command
{
public MyToolWindowCommand(VisualStudioExtensibility extensibility, string name)
: base(extensibility, name)
{
}
public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
// "Activate: true" here means that the tool window will be shown and given focus
// "Activate: false" here means that the tool window will only be shown which may result in only showing its tab
await this.Extensibility.Shell().ShowToolWindowAsync<MyToolWindow>(activate: true, cancellationToken);
}
}
```
See the [Commands](./../command/command.md) docs to learn more about setting up commands.
### Registering a tool window with dynamic visibility
Another mechanism for opening a tool window is to set up activation constraints for when it should be shown or hidden. This allows tool windows to automatically be opened when certain conditions are met, and hidden again when those conditions are no longer applicable.
The attribute [Microsoft.VisualStudio.Extensibility.ToolWindows.ToolWindowVisibleWhenAttribute](./../../api/Microsoft.VisualStudio.Extensibility.md/#toolwindowvisiblewhenattribute-type) has a few parameters that you should become familiar with:
| Parameter | Type | Required | Description |
| --------- |----- | -------- | ----------- |
| Expression | String | Yes | A boolean expression string which, when true, will mean the context is active and the tool window will be shown. |
| TermNames | String[] | Yes | The names of the terms used in the expression. |
| TermValues | String[] | Yes | The values of each term. The term values must be in the same order as term names array. |
```csharp
// The tool window will be shown if a .cs file is the active document, and
// will be hidden whenever any other file type is the active document.
[ToolWindow]
[ToolWindowVisibleWhen("FileSelected",
new string[] { "FileSelected" },
new string[] { "ClientContext:Shell.ActiveSelectionFileName=.cs$" })]
public class MyToolWindow : ToolWindow
{
// TODO: Implement the rest of the class
}
```
See the [Using rule based activation constraints](../../inside-the-sdk/activation-constraints.md/#rule-based-activation-constraints) docs for more information on valid term values.

Просмотреть файл

@ -1,7 +1,7 @@
---
title: Creating your first extension reference
description: A reference for creating your first OOP extension
date: 2021-8-16
date: 2022-07-28
---
# Creating your first out-of-process Visual Studio extension
@ -9,85 +9,40 @@ date: 2021-8-16
## Introduction
This document is a quick walkthrough on how to create your first extension using the new out-of-process extensibility model.
The complete project can be found at [SimpleRemoteCommandSample](https://github.com/microsoft/VSExtensibility/tree/main/New_Extensibility_Model/Samples/SimpleRemoteCommandSample) but these steps will help you understand the requirements and dependencies.
## Prerequisites
* Visual Studio 2022 Preview 3 or higher with managed languages workload.
* Visual Studio 2022.4 Preview 1 or higher with `.Net desktop development` workload.
* Install [VisualStudio.Extensibility Project System](https://marketplace.visualstudio.com/items?itemName=vsext.gladstone): This extension will allow you to debug extension projects using F5. There is currently no other deployment mechanism supported.
## Create the extension project
* The extensibility APIs are distributed via [vssdk nuget repository](https://dev.azure.com/azure-public/vside/_artifacts/feed/vssdk/connect/dotnet) to allow for more frequent updates to libraries with preview versions of Visual Studio. Please make sure that your `nuget.config` has the necessary package source entry as shown below.
* Use `VisualStudio.Extensibility Project` template to create a new extensibility project.
```xml
<add key="vssdk" value="https://pkgs.dev.azure.com/azure-public/vside/_packaging/vssdk/nuget/v3/index.json" />
```
* All of the API components can be referenced by [nuget packages](https://dev.azure.com/azure-public/vside/_artifacts/feed/vssdk/NuGet/Microsoft.VisualStudio.Extensibility.Sdk/overview/17.3.46-alpha), so you can start with an empty .NET 6.0 C# class library project.
* Once project is created change `TargetFramework` from `net6.0` to `net6.0-windows` by editing project file or changing Target OS to `Windows` in project properties.
![Target Framework Properties](target-framework-properties.png "Target Framework Properties")
* Add references to `Microsoft.VisualStudio.Extensibility.Sdk` and `Microsoft.VisualStudio.Extensibility.Build` nuget package through adding the following section to your project file.
```
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.xxxxx" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.xxxxx" />
</ItemGroup>
```
* To ensure extension dependencies are copied locally, set the `CopyLocalLockFileAssemblies` property in the project file:
```
<PropertyGroup>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
```
* Add a [source.extension.vsixmanifest](https://github.com/microsoft/VSExtensibility/tree/main/New_Extensibility_Model/Samples/SimpleRemoteCommandSample/source.extension.vsixmanifest) file to describe your extension including name, description and unique identifier. This information in the future will be used to list the extension in the gallery.
![VSExtensibilityTemplate](vsextensibility-project-template.png "VisualStudio.Extensibility template")
At this point you are ready to start extending Visual Studio by adding commands and editor components to your extension.
## Add a command handler
In this step we will add a new command to Visual Studio to perform an action when user executes the command. In this case the command will be represented by a new menu item under `Tools` menu as specified by the placement parameter below.
## Add your first command
* Create a new `.cs` file and include the following code:
The template creates `Command1.cs` as your first command handler which you can use as a starting point. The default attributes shown below will place the command in `Tools` menu with an extension icon.
```csharp
namespace SimpleRemoteCommandSample
[CommandIcon(KnownMonikers.Extension, IconSettings.IconAndText)]
[Command("Extension1.Command1", "Sample Remote Command", placement: CommandPlacement.ToolsMenu)]
```
When the command is executed, Visual Studio will call in to `ExecuteCommandAsync` method where you can place a breakpoint. You can utilize `context` argument or `this.Extensibility` object to interact with Visual Studio.
For example, an example command handler could be as below:
```csharp
public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Commands;
using Microsoft.VisualStudio.Extensibility.Definitions;
[CommandIcon("Extension", IconSettings.IconAndText)]
[Command(
"SimpleRemoteCommandSample.Command",
"Sample Remote Command",
placement: KnownCommandPlacement.ToolsMenu)]
public class CommandHandler : Command
{
private TraceSource traceSource;
public CommandHandler(VisualStudioExtensibility extensibility, TraceSource traceSource, string name)
: base(extensibility, name)
{
this.traceSource = Requires.NotNull(traceSource, nameof(traceSource));
}
public override Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
this.traceSource.TraceInformation($"Hello from another process!");
return Task.CompletedTask;
}
}
await context.ShowPromptAsync(
"Hello from another process!",
PromptOptions.OK,
cancellationToken);
}
```
@ -96,9 +51,7 @@ For more information on how to add commands, please refer to [Commands](../exten
## Debug your extension
* Making sure that your extension project is selected as startup project in Visual Studio, press `F5` to start debugging.
* This will build your extension and deploy it to the experimental instance of Visual Studio version you are using. The debugger should attach once your extension is loaded.
* You can find the command in `Tools` menu as shown.
![SampleCommand](extension-command.png "Sample command")
![SampleCommand](extension-command.png "Sample Remote Command")

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 58 KiB

Просмотреть файл

@ -0,0 +1,287 @@
---
title: Advanced *Remote UI* concepts
description: A walkthrough of more advanced *Remote UI* concepts
date: 2022-7-20
---
# Setting up the project
In this walkthrough, we will go over advanced *Remote UI* concepts by incrementally modifying a tool window that shows a list of random colors:
![Random colors tool window](colors-tool-window.png "Random colors tool window")
We will discuss:
- How multiple *async commands* executions can run in parallel and how to disable UI elements when a command is running.
- How to bind multiple buttons to the same *async command*.
- How reference types are handled in the *Remote UI* data context and its proxy.
- How to use an *async command* as an event handler.
- How to disable a single button when its *async command*'s callback is executing if multiple buttons are bound to the same command.
- How to use WPF types, like complex brushes, in the *Remote UI* data context.
- How *Remote UI* handles threading.
This walkthrough is based on the introductory [*Remote UI*](remote-ui.md) article and expects that you have a working *VisualStudio.Extensibility* extension including:
1. a `.cs` file for the command which opens the tool window,
1. a `MyToolWindow.cs` file for the `ToolWindow` class,
1. a `MyToolWindowContent.cs` file for the `RemoteUserControl` class,
1. a `MyToolWindowContent.xaml` embedded resource file for the `RemoteUserControl` xaml definition,
1. a `MyToolWindowData.cs` file for the data context of the `RemoteUserControl`.
Let's start with updating `MyToolWindowContent.xaml` to show a list view and a button":
```xml
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsshell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:vsfx="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0">
<Grid x:Name="RootGrid">
<Grid.Resources>
<Style TargetType="ListView" BasedOn="{StaticResource {x:Static vsshell:VsResourceKeys.ThemedDialogListViewStyleKey}}" />
<Style TargetType="Button" BasedOn="{StaticResource {x:Static vsshell:VsResourceKeys.ButtonStyleKey}}" />
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsshell:VsBrushes.WindowTextKey}}" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView ItemsSource="{Binding Colors}" HorizontalContentAlignment="Stretch">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ColorText}" />
<Rectangle Fill="{Binding Color}" Width="50px" Grid.Column="1" />
<Button Content="Remove" Grid.Column="2" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Add color" Command="{Binding AddColorCommand}" Grid.Row="1" />
</Grid>
</DataTemplate>
```
Then, let's update the data context class `MyToolWindowData.cs`:
```CSharp
using Microsoft.VisualStudio.Extensibility.UI;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Text;
using System.Windows.Media;
namespace MyToolWindowExtension;
[DataContract]
internal class MyToolWindowData
{
private Random random = new();
public MyToolWindowData()
{
AddColorCommand = new AsyncCommand(async (parameter, cancellationToken) =>
{
await Task.Delay(TimeSpan.FromSeconds(2));
var color = new byte[3];
random.NextBytes(color);
Colors.Add(new MyColor(color[0], color[1], color[2]));
});
}
[DataMember]
public ObservableCollection<MyColor> Colors { get; } = new();
[DataMember]
public AsyncCommand AddColorCommand { get; }
[DataContract]
public class MyColor
{
public MyColor(byte r, byte g, byte b)
{
ColorText = Color = $"#{r:X2}{g:X2}{b:X2}";
}
[DataMember]
public string ColorText { get; }
[DataMember]
public string Color { get; }
}
}
```
There are just a few noteworthy things in this code:
1. `MyColor.Color` is a `string` but it is used as a `Brush` when data bound in XAML, this is a capability provided by WPF.
1. The `AddColorCommand` async callback contains a 2 seconds delay to simulate a long-running operation.
1. We use [ObservableCollection\<T\>](https://docs.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1), which is supported by *Remote UI*, to dynamically update the list view.
1. `MyToolWindowData` and `MyColor` don't implement [INotifyPropertyChanged](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged) because, at the moment, all properties are readonly.
# Handling long-running async commands
One of the most important differences between *Remote UI* and normal WPF is that all operations that involve communication between the UI and the extension are async.
*Async commands* like `AddColorCommand` make this explicit by providing an async callback.
We can notice the effect of this if we click the "Add color" button multiple times in a short time: since each command execution takes 2 seconds, multiple executions will happen in parallel and multiple colors will appear in the list together when the 2 second delay is over. This may give the impression to the user that the "Add color" button is not working.
![Overlapped async command execution](overlapped-async-commands.gif "Overlapped async command execution")
To address this, we will disable the button while the *async command* is executing. The most straightforward way to do this is to simply set `CanExecute` for the command to false:
```CSharp
AddColorCommand = new AsyncCommand(async (parameter, ancellationToken) =>
{
AddColorCommand!.CanExecute = false;
try
{
await Task.Delay(TimeSpan.FromSeconds(2));
var color = new byte[3];
random.NextBytes(color);
Colors.Add(new MyColor(color[0], color[1], color[2]));
}
finally
{
AddColorCommand.CanExecute = true;
}
});
```
This solution still has imperfect synchronization since, when the user clicks the button, the command callback is executed asynchronously in the extension, the callback sets `CanExecute` to `false` which is then propagated asynchronously to the proxy data context in the Visual Studio process resulting in the button being disabled. The user could click the button twice in rapid succession before the button is disabled.
A better solution is to leverage the `RunningCommandsCount` property of *async commands*:
```xml
<Button Content="Add color" Command="{Binding AddColorCommand}" IsEnabled="{Binding AddColorCommand.RunningCommandsCount.IsZero}" Grid.Row="1" />
```
`RunningCommandsCount` is a counter of how many concurrent async executions of the command are currently underway. This counter is incremented on the UI thread as soon as the button is clicked, which allows to synchronously disable the button by binding `IsEnabled` to `RunningCommandsCount.IsZero`.
Because all *Remote UI* commands execute asynchronously, the best practice is to always use `RunningCommandsCount.IsZero` to disable controls when appropriate, even if the command is expected to complete quickly.
# *Async commands* and data templates
Now let's implement the "Remove" button which allows the user to delete an entry from the list. We can either create one *async command* for each `MyColor` object or we can have a single *async command* in `MyToolWindowData` and use a parameter to identify which color should be removed. The latter option is a cleaner design, so let's implement that.
First we update the button XAML in the data template:
```xml
<Button Content="Remove" Grid.Column="2"
Command="{Binding DataContext.RemoveColorCommand,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}"
CommandParameter="{Binding}"
IsEnabled="{Binding DataContext.RemoveColorCommand.RunningCommandsCount.IsZero,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}}" />
```
Then we add the corresponding `AsyncCommand` to `MyToolWindowData`:
```CSharp
[DataMember]
public AsyncCommand RemoveColorCommand { get; }
```
And set the command's async callback in the constructor of `MyToolWindowData`:
```CSharp
RemoveColorCommand = new AsyncCommand(async (parameter, ancellationToken) =>
{
await Task.Delay(TimeSpan.FromSeconds(2));
Colors.Remove((MyColor)parameter!);
});
```
We are again using a `Task.Delay` to simulate a long running *async command* execution.
# Reference types in the data context
In the code above, a `MyColor` object is received as the parameter of an *async command* and used as parameter of a `List<T>.Remove` call which employs reference equality (since `MyColor` is a rererence type that doesn't override `Equals`) to identify the element to remove. This is possible because, even if the parameter is received from the UI, the exact instance of `MyColor` that is currently part of the data context is received, not a copy.
The processes of
- proxying the data context of a *remote user control*;
- sending `INotifyPropertyChanged` updates from the extension to Visual Studio or vice versa;
- sending observable collection updates from the extension to Visual Studio, or vice versa;
- sending *async command* parameters
all honor the identity of reference type objects. With the exception of strings, reference type objects are never duplicated when transferred back to the extension.
![Remote UI data binding reference types](remote-ui-databinding-references.png "Remote UI data binding reference types")
In the picture above, you can see how every reference type object in the data context (the commands, the collection, each `MyColor` and even the entire data context) is assigned a unique identifier by the *Remote UI* infrastructure. When the user clicks the "Remove" button for the proxy color object *#5*, the unique indentifier (*#5*), not the value of the object, is sent back to the extension. The *Remote UI* infrastructure takes care of retrieving the corresponding `MyColor` object and passing it as parameter to the *async command*'s callback.
# RunningCommandsCount with multiple bindings and event handling
If we test the extension at this point, we will notice that when one of the "Remove" buttons is clicked, all "Remove" buttons are disabled:
![Async Command with multiple bindings](async-commands-multiple-bindings.gif "Async Command with multiple bindings")
This may be the desired behavior. But, let's say that we want only the current button to be disabled and we will allow the user to queue multiple colors for removal: we cannot use the *async command*'s `RunningCommandsCount` property because we have a single command shared between all the buttons.
We can achieve our goal by attaching a `RunningCommandsCount` property to each button so that we have a separate counter for each color. First, we need to add a new `xmlns` (`http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml`) to consume *Remote UI* types from XAML:
```xml
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsshell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:vsfx="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml">
```
Then, we change the "Remove" button to the following:
```xml
<Button Content="Remove" Grid.Column="2"
IsEnabled="{Binding Path=(vs:ExtensibilityUICommands.RunningCommandsCount).IsZero, RelativeSource={RelativeSource Self}}">
<vs:ExtensibilityUICommands.EventHandlers>
<vs:EventHandlerCollection>
<vs:EventHandler Event="Click"
Command="{Binding DataContext.RemoveColorCommand, ElementName=RootGrid}"
CommandParameter="{Binding}"
CounterTarget="{Binding RelativeSource={RelativeSource Self}}" />
</vs:EventHandlerCollection>
</vs:ExtensibilityUICommands.EventHandlers>
</Button>
```
The `vs:ExtensibilityUICommands.EventHandlers` attached property allows assigning *async commands* to any event (for example, `MouseRightButtonUp`) and can be useful in more advanced scenarios.
`vs:EventHandler` can also have a `CounterTarget`: a `UIElement` to which a `vs:ExtensibilityUICommands.RunningCommandsCount` property should be attached, counting the active executions related to that specific event.
In this case, we use `vs:EventHandler` to attach to each button its own separate counter of active command executions. By binding `IsEnabled` to the attached property, only that specific button is disabled when the corresponding color is being removed:
![Async Command with targeted RunningCommandsCount](targeted-counter.gif "Async Command with targeted RunningCommandsCount")
# Using WPF types in the data context
Until now, the data context of our *remote user control* has been composed of primitives (numbers, strings, etc.), observable collections and our own classes marked with `DataContract`. It is sometimes useful to include simple WPF types in the data context like complex brushes.
Because a *VisualStudio.Extensibility* extension may not even run in the Visual Studio process, it cannot share WPF objects directly with its UI. The extension may not even have access to WPF types since it can target `netstandard2.0` or `net6.0` (not the `-windows` variant).
*Remote UI* provides the `XamlFragment` type which allows including a XAML definition of a WPF object in the data context of a *remote user control*:
```CSharp
[DataContract]
public class MyColor
{
public MyColor(byte r, byte g, byte b)
{
ColorText = $"#{r:X2}{g:X2}{b:X2}";
Color = new(@$"<LinearGradientBrush xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation""
StartPoint=""0,0"" EndPoint=""1,1"">
<GradientStop Color=""Black"" Offset=""0.0"" />
<GradientStop Color=""{ColorText}"" Offset=""0.7"" />
</LinearGradientBrush>");
}
[DataMember]
public string ColorText { get; }
[DataMember]
public XamlFragment Color { get; }
}
```
With the code above, the `Color` property value will be converted to a `LinearGradientBrush` object in the data context proxy:
![WPF types in data context](wpf-types-in-data-context.png "WPF types in data context")
# *Remote UI* and threads
*Async command* callbacks (and `INotifyPropertyChanged` callbacks for values updated by the UI through data biding) are raised on random threadpool threads. Callbacks are raised one at a time and won't overlap until the code yields control (using an `await` expression).
This behavior can be changed by passing a [NonConcurrentSynchronizationContext](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.threading.nonconcurrentsynchronizationcontext) to the `RemoteUserControl` constructor. In that case, the provided synchronization context will be used for all *async command* and `INotifyPropertyChanged` callbacks related to that control.

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 37 KiB

Двоичные данные
docs/new-extensibility-model/inside-the-sdk/colors-tool-window.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 12 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 187 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 40 KiB

Двоичные данные
docs/new-extensibility-model/inside-the-sdk/remote-ui-commands.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 93 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 4.6 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 500 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 502 KiB

Двоичные данные
docs/new-extensibility-model/inside-the-sdk/remote-ui-themed.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.6 KiB

Просмотреть файл

@ -0,0 +1,394 @@
---
title: *Remote UI*
description: A walkthrough exploring the *Remote UI* features
date: 2022-7-18
---
# Why *Remote UI*
One of the main goals of the VisualSudio.Extensibility model is to allow extensions to run outside of the Visual Studio process. This introduces an obstacle for adding UI support to extensions since most UI frameworks are in-process.
*Remote UI* is a set of classes allowing to define WPF controls in an *out-of-proc* extension and showing them as part of the Visual Studio UI.
*Remote UI* leans heavily towards the *Model-View-ViewModel* design pattern relying on XAML and data binding, commands (instead of events), and triggers (instead of interacting with the [logical tree](https://docs.microsoft.com/en-us/dotnet/desktop/wpf/advanced/trees-in-wpf) from *code-behind*).
While *Remote UI* was developed to support *out-of-proc* extensions, *VisualStudio.Extensibility* APIs that rely on *Remote UI*, like `ToolWindow`, will leverage *Remote UI* for *in-proc* extensions as well.
The main differences between *Remote UI* and normal WPF development are:
- Most *Remote UI* operations, including binding to the data context and command execution, are asynchronous.
- When defining data types to be used in *Remote UI* data contexts they must be decorated with the `DataContract` and `DataMember` attributes.
- *Remote UI* doesn't allow referencing your own custom controls.
- A *Remote user control* is fully defined in a single XAML file which references a single (but potentially complex and nested) data context object.
- *Remote UI* doesn't support code behind or event handlers (workarounds are descrived in the [advanced *Remote UI* concepts](advanced-remote-ui.md) document).
- A *Remote user control* is actually instantiated in the Visual Studio process, not the process hosting the extension: the XAML cannot reference types and assemblies from the extension but can reference types and assemblies from the Visual Studio process.
# Creating a *Remote UI* Hello World extension
Let's start creating the most basic *Remote UI* extension.
Follow the instructions in [Creating your first out-of-process Visual Studio extension](../getting-started/create-your-first-extension.md).
You should now have a working extension with a single command, the next step is to add a `ToolWindow` and a `RemoteUserControl`. The `RemoteUserControl` is the *Remote UI* equivalent of a WPF user control.
You will end up with 4 files:
1. a `.cs` file for the command which opens the tool window,
1. a `.cs` file for the `ToolWindow` which provides the `RemoteUserControl` to Visual Studio,
1. a `.cs` file for the `RemoteUserControl` which references its XAML definition,
1. a `.xaml` file for the `RemoteUserControl`.
Later on we will add a data context for the `RemoteUserControl` which represents the *ViewModel* in the MVVM pattern.
## Updating the command
Update the code of the command to show the tool window using the `ShowToolWindowAsync`:
```CSharp
public override Task ExecuteCommandAsync(IClientContext context, CancellationToken ancellationToken)
{
return Extensibility.Shell().ShowToolWindowAsync<MyToolWindow>(activate: true, cancellationToken);
}
```
You can also consider changing the `Command` attribute for a more appropriate display message and placement:
```CSharp
[Command(nameof(ShowMyToolWindowCommand), "Show Hello World Tool Window", placement: CommandPlacement.ViewOtherWindowsMenu)]
internal class ShowMyToolWindowCommand : Command
{
```
## Creating the tool window
Create a new `MyToolWindow.cs` file and define a `MyToolWindow` class extending `ToolWindow`.
The `GetContentAsync` method is supposed to return an `IRemoteUserControl` which we will define in the next step. Since the *remote user control* is disposable, let's also take care of disposing it by overriding the `Dispose(bool)` method.
```CSharp
namespace MyToolWindowExtension;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.ToolWindows;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;
[ToolWindow]
internal class MyToolWindow : ToolWindow
{
private readonly MyToolWindowContent content = new();
public MyToolWindow(VisualStudioExtensibility extensibility)
: base(extensibility)
{
Title = "My Tool Window";
}
public override async Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
=> content;
public override Task InitializeAsync(CancellationToken cancellationToken)
=> Task.CompletedTask;
protected override void Dispose(bool disposing)
{
if (disposing)
content.Dispose();
base.Dispose(disposing);
}
}
```
## Creating the *remote user control*
This step is performed across three files.
### *Remote user control* class
The *remote user control* class, named `MyToolWindowContent`, is straightforward:
```CSharp
namespace MyToolWindowExtension;
using Microsoft.VisualStudio.Extensibility.UI;
internal class MyToolWindowContent : RemoteUserControl
{
public MyToolWindowContent()
: base(dataContext: null)
{
}
}
```
We don't need a data context yet, so we can set it to `null` for now.
A class extending `RemoteUserControl`, will automatically use the XAML embedded resource with the same name. If we wanted to change this behavior, we can override the `GetXamlAsync` method.
### XAML definition
Next, let's create a file named `MyToolWindowContent.xaml`:
```xml
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Label>Hello World</Label>
</DataTemplate>
```
As described above, this file must have the same name as the `RemoteUserControl` class.
The XAML definition of the *remote user control* is normal WPF xaml describing a `DataTemplate`. This XAML will be sent to Visual Studio and used to fill the tool window content.
### Setting the XAML as an embedded resource
Finally, let's open the `.csproj` file and make sure that the XAML file is treated as an embedded resource:
```xml
<ItemGroup>
<EmbeddedResource Include="MyToolWindowContent.xaml" />
</ItemGroup>
```
You can also change the target framework for your extension from `net6.0` to `net6.0-windows` in order to get better autocompletion in the XAML file.
## Testing the extension
You should now be able to press F5 and debug the extension.
![Menu and tool window](hello-world-tool-window.png "Menu and tool window")
# Adding support for themes
It is a good idea to write our UI keeping in mind that Visual Studio can be themed resulting in very different colors being used.
Let's update the XAML to use the [styles](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.shell.vsresourcekeys) and [colors](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.platformui.environmentcolors) used across Visual Studio:
```xml
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsshell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:vsfx="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0">
<Grid>
<Grid.Resources>
<Style TargetType="Label" BasedOn="{StaticResource {x:Static vsshell:VsResourceKeys.ThemedDialogLabelStyleKey}}" />
</Grid.Resources>
<Label>Hello World</Label>
</Grid>
</DataTemplate>
```
The label now uses the same theme as the rest of the Visual Studio UI and automatically changes color if switching to dark mode:
![Themed tool window](remote-ui-themed.png "Themed tool window")
The `xmlns`' above reference the [Microsoft.VisualStudio.Shell.15.0](https://www.nuget.org/packages/Microsoft.VisualStudio.Shell.15.0) assembly which is not one of the extension dependencies. This is fine because this XAML will be used by the Visual Studio process, which has a dependencies on *Shell.15*, not by the extension itself.
In order to get a better XAML editing experience, you can **temporarily** add a `PackageReference` to `Microsoft.VisualStudio.Shell.15.0` to the extension project. **Don't forget to remove it** later since an *out-of-proc* *VisualStudio.Extensibility* extension shouldn't reference this package!
# Adding a data context
Let's now add a data context class for the *remote user control*:
```CSharp
using System.Runtime.Serialization;
namespace MyToolWindowExtension;
[DataContract]
internal class MyToolWindowData
{
[DataMember]
public string? LabelText { get; init; }
}
```
and update `MyToolWindowContent.cs` and `MyToolWindowContent.xaml` to use it:
```CSharp
internal class MyToolWindowContent : RemoteUserControl
{
public MyToolWindowContent()
: base(dataContext: new MyToolWindowData { LabelText = "Hello Binding!"})
{
}
```
```xml
<Label Content="{Binding LabelText}" />
```
The content of the label is now set through databinding:
![Tool window with data binding](remote-ui-data-binding.png "Tool window with data binding")
The data context type above is marked with `DataContract` and `DataMember` attributes. This is because the `MyToolWindowData` instance exists in the extension host process while the WPF control created from `MyToolWindowContent.xaml` exists in the Visual Studio process. To make data binding work, the *Remote UI* infrastructure generates a proxy of the `MyToolWindowData` object in the Visual Studio process. The `DataContract` and `DataMember` attributes indicate which types and properties are relevant for data binding and should be replicated in the proxy.
The data context of the *remote user control* is passed as a constructor parameter of the `RemoteUserControl` class: the `RemoteUserControl.DataContext` property is read-only. This doesn't imply that the whole data context is immutable, but the root data context object of a *remote user control* cannot be replaced. In the next section we will make `MyToolWindowData` mutable and observable.
# Lifecycle of a Remote User Control
You can override the `ControlLoadedAsync` method to be notified when the control is first loaded in a WPF container. If in your implementation, the state of the data context may change independently from UI events, the `ControlLoadedAsync` method is the right place to initialize the content of the data context and start applying changes to it.
You can also override the `Dispose` method to be notified when the control is destroyed and won't be used anymore.
```CSharp
internal class MyToolWindowContent : RemoteUserControl
{
public MyToolWindowContent()
: base(dataContext: new MyToolWindowData())
{
}
public override async Task ControlLoadedAsync(CancellationToken cancellationToken)
{
await base.ControlLoadedAsync(cancellationToken);
// Your code here
}
protected override void Dispose(bool disposing)
{
// Your code here
base.Dispose(disposing);
}
}
```
# Commands, observability and two-way data binding
Next, let's make the data context observable and add a button to the toolbox.
The data context can be made observable by implementing [INotifyPropertyChanged](https://docs.microsoft.com/en-us/dotnet/api/system.componentmodel.inotifypropertychanged). Alternatively, *Remote UI* provides a convenient abstract class, `NotifyPropertyChangedObject`, that we can extend to reduce boilerplate code.
A data context usually has a mix of readonly properties and observable properties. The data context can be a complex graph of objects as long as they are marked with the `DataContract` and `DataMember` attributes and implement `INotifyPropertyChanged` as necessary. It is also possible to have [observable collections](https://docs.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.observablecollection-1).
We also need to add a command to the data context. In *Remote UI*, commands implement `IAsyncCommand` but it is often easier to simply create an instance of the `AsyncCommand` class.
`IAsyncCommand` differs from [ICommand](https://docs.microsoft.com/en-us/dotnet/api/system.windows.input.icommand) in two ways:
1. The `Execute` method is replaced with `ExecuteAsync` because everything in *Remote UI* is async!
1. The `CanExecute(object)` method is replaced by a `CanExecute` property. The `AsyncCommand` class takes care of making `CanExecute` observable.
It's important to note that *Remote UI* doesn't support event handlers, so all notifications from the UI to the extension must be implemented through databinding and commands.
This is the resulting code for `MyToolWindowData`.
```CSharp
[DataContract]
internal class MyToolWindowData : NotifyPropertyChangedObject
{
public MyToolWindowData()
{
HelloCommand = new((parameter, cancellationToken) =>
{
Text = $"Hello {Name}!";
return Task.CompletedTask;
});
}
private string _name = string.Empty;
[DataMember]
public string Name
{
get => _name;
set => SetProperty(ref this._name, value);
}
private string _text = string.Empty;
[DataMember]
public string Text
{
get => _text;
set => SetProperty(ref this._text, value);
}
[DataMember]
public AsyncCommand HelloCommand { get; }
}
```
We also need to fix the `MyToolWindowContent` constructor:
```CSharp
public MyToolWindowContent()
: base(dataContext: new MyToolWindowData())
{
}
```
Let's update `MyToolWindowContent.xaml` to use the new properties in the datacontext. This is all normal WPF XAML. Even the `IAsyncCommand` object is proxied as an `ICommand` in the Visual Studio process so it can be data-bound as usual.
```xml
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vsshell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:vsfx="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0">
<Grid>
<Grid.Resources>
<Style TargetType="Label" BasedOn="{StaticResource {x:Static vsshell:VsResourceKeys.ThemedDialogLabelStyleKey}}" />
<Style TargetType="TextBox" BasedOn="{StaticResource {x:Static vsshell:VsResourceKeys.TextBoxStyleKey}}" />
<Style TargetType="Button" BasedOn="{StaticResource {x:Static vsshell:VsResourceKeys.ButtonStyleKey}}" />
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsshell:VsBrushes.WindowTextKey}}" />
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Content="Name:" />
<TextBox Text="{Binding Name}" Grid.Column="1" />
<Button Content="Say Hello" Command="{Binding HelloCommand}" Grid.Column="2" />
<TextBlock Text="{Binding Text}" Grid.ColumnSpan="2" Grid.Row="1" />
</Grid>
</DataTemplate>
```
![Tool window with two-way binding and a command](remote-ui-commands.gif "Tool window with two-way binding and a command")
## Understanding asynchronicity in *Remote UI*
The whole *Remote UI* communication for this tool window follows these steps:
1. The data context is proxied inside the Visual Studio process with its original content,
1. The control created from `MyToolWindowContent.xaml` is data bound to the data context proxy,
1. The user types some text in the text box which is assigned to the `Name` property of the data context proxy through databinding. The new value of `Name` is propagated to the `MyToolWindowData` object.
1. The user clicks the button causing a cascade of effects:
- the `HelloCommand` in the data context proxy is executed,
- the async execution of the extender's `AsyncCommand` code is started,
- the async callback for `HelloCommad` updates the value of the observable property `Text`,
- the new value of `Text` is propagated to the data context proxy,
- the text block in the tool window is updated to the new value of `Text` through data binding.
![Tool window two-way binding and commands communication](remote-ui-databinding-commands.png "Tool window two-way binding and commands communication")
## Using command parameters to avoid race conditions
All the operations that involve communication between Visual Studio and the extension (blue arrows in the diagram) are asynchronous. It's important to consider this aspect in the overall design of the extension.
For this reason, if consistency is important, it is better to use command parameters, instead of two-way binding, to retrieve data context state at the time of the execution of a command.
Let's make this change by binding the button's `CommandParameter` to `Name`:
```xml
<Button Content="Say Hello" Command="{Binding HelloCommand}" CommandParameter="{Binding Name}" Grid.Column="2" />
```
Then, let's modify the command's callback to use the parameter.
```CSharp
HelloCommand = new AsyncCommand((parameter, cancellationToken) =>
{
Text = $"Hello {(string)parameter!}!";
return Task.CompletedTask;
});
```
With this approach the value of the `Name` property is retrieved synchronously from the data context proxy at the time of the button click and sent over to the extension. This avoids any race conditions, especially if the `HelloCommand` callback is changed in the future to yield (have `await` expressions).
## *Async commands* consuming data from multiple properties
Using a command parameter is not an option if the command needs to consume multiple properties that are settable by the user. For example, if the UI had two textboxes: "First Name" and "Last Name".
The solution in this case is to retrieve, in the *async command* callback, the value of all the properties from the data context before yielding.
Below you can see a sample where the `FirstName` and `LastName` property values are retrieved before yielding to make sure that the value at the time of the command invocation is used:
```CSharp
HelloCommand = new(async (parameter, cancellationToken) =>
{
string firstName = FirstName;
string lastName = LastName;
await Task.Delay(TimeSpan.FromSeconds(1));
Text = $"Hello {firstName} {lastName}!";
});
```
It's also important to avoid the extension asynchronously updating the value of properties that can also be updated by the user. In other words, avoid [TwoWay](https://docs.microsoft.com/en-us/dotnet/api/system.windows.data.bindingmode) data binding.
# Advanced *Remote UI* concepts
The information above should be enough to build simple *Remote UI* components. For more advanced scenarios see [advanced *Remote UI* concepts](advanced-remote-ui.md).

Двоичные данные
docs/new-extensibility-model/inside-the-sdk/targeted-counter.gif Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 51 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 15 KiB