* Add sample for Settings API

---------

Co-authored-by: Charles Willis <chawill@microsoft.com>
This commit is contained in:
Charles Willis 2024-09-09 18:40:48 +01:00 коммит произвёл GitHub
Родитель 23317634f0
Коммит 8b4ca15be7
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
10 изменённых файлов: 459 добавлений и 0 удалений

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

@ -0,0 +1,14 @@
{
"SettingsSample.MyToolWindowCommand.DisplayName": "Sample Text Tool Window",
"SettingsSample.Settings.Category.DisplayName": "Settings Sample",
"SettingsSample.Settings.Category.Description": "Settings for the LoremIpsum sample generator.",
"SettingsSample.Settings.AutoUpdate.DisplayName": "Auto Update",
"SettingsSample.Settings.AutoUpdate.Description": "Whether to update the sample text when a setting changes.",
"SettingsSample.Settings.TextLength.DisplayName": "Text Length",
"SettingsSample.Settings.TextLength.Description": "Number of characters to include in the generated text.",
"SettingsSample.Settings.QuoteStyle.DisplayName": "Quote Style",
"SettingsSample.Settings.QuoteStyle.Description": "Style of quotes to enclose the generated text.",
"SettingsSample.Settings.QuoteStyle.None": "None",
"SettingsSample.Settings.QuoteStyle.Single": "Single",
"SettingsSample.Settings.QuoteStyle.Double": "Double"
}

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

@ -0,0 +1,47 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace SettingsSample;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.ToolWindows;
using Microsoft.VisualStudio.RpcContracts.RemoteUI;
/// <summary>
/// A sample tool window.
/// </summary>
[VisualStudioContribution]
public class MyToolWindow : ToolWindow
{
private MyToolWindowData? dataContext;
/// <summary>
/// Initializes a new instance of the <see cref="MyToolWindow" /> class.
/// </summary>
public MyToolWindow()
{
this.Title = "Settings Sample Tool Window";
}
/// <inheritdoc />
public override ToolWindowConfiguration ToolWindowConfiguration => new()
{
Placement = ToolWindowPlacement.DocumentWell,
AllowAutoCreation = false,
};
/// <inheritdoc />
public override Task InitializeAsync(CancellationToken cancellationToken)
{
this.dataContext = new MyToolWindowData(this.Extensibility);
return this.dataContext.InitializeAsync(cancellationToken);
}
/// <inheritdoc />
public override Task<IRemoteUserControl> GetContentAsync(CancellationToken cancellationToken)
{
return Task.FromResult<IRemoteUserControl>(new MyToolWindowControl(this.dataContext));
}
}

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

@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace SettingsSample;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Commands;
/// <summary>
/// A sample command for showing a tool window.
/// </summary>
[VisualStudioContribution]
public class MyToolWindowCommand : Command
{
/// <inheritdoc />
public override CommandConfiguration CommandConfiguration => new("%SettingsSample.MyToolWindowCommand.DisplayName%")
{
Placements = [CommandPlacement.KnownPlacements.ToolsMenu],
Icon = new(ImageMoniker.KnownValues.ToolWindow, IconSettings.IconAndText),
};
/// <inheritdoc />
public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
await this.Extensibility.Shell().ShowToolWindowAsync<MyToolWindow>(activate: true, cancellationToken);
}
}

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

@ -0,0 +1,24 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace SettingsSample;
using System.Threading;
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>
public MyToolWindowControl(object? dataContext)
: base(dataContext)
{
}
}

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

@ -0,0 +1,31 @@
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vs="http://schemas.microsoft.com/visualstudio/extensibility/2022/xaml"
xmlns:styles="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:colors="clr-namespace:Microsoft.VisualStudio.PlatformUI;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="Vertical"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Margin="0,0,5,0"
Text="Sample Text:"
VerticalAlignment="Center" />
<TextBlock x:Name="SampleTextBlock"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding SampleText, Mode=OneWay}"
Style="{StaticResource {x:Static styles:VsResourceKeys.TextBlockEnvironment155PercentFontSizeStyleKey}}">
</TextBlock>
<Button x:Name="UpdateButton"
Content="Update Sample Text From Settings"
IsEnabled="{Binding ManualUpdate}"
Margin="0,10,0,0"
MinHeight="25"
MinWidth="60"
Style="{StaticResource {x:Static styles:VsResourceKeys.ButtonStyleKey}}"
Command="{Binding UpdateCommand}" />
</StackPanel>
</Border>
</DataTemplate>

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

@ -0,0 +1,139 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace SettingsSample;
using System;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Settings;
using Microsoft.VisualStudio.Extensibility.UI;
/// <summary>
/// A sample data context object to use with tool window UI content.
/// </summary>
[DataContract]
internal class MyToolWindowData : NotifyPropertyChangedObject
{
internal static readonly string LoremIpsumText = """
Lorem ipsum dolor sit amet consectetur adipiscing elit dignissim lacinia donec,
praesent diam ac mattis morbi nisi dictum dapibus. Convallis natoque interdum
curabitur malesuada tellus aliquam dignissim hendrerit tempor, primis sem nulla
neque cubilia rutrum mollis nisl, eleifend imperdiet lacinia fames gravida sed mus
magnis. Pretium aliquet consequat curabitur eros eleifend praesent, nostra malesuada
hendrerit ornare volutpat, cubilia aptent at mollis convallis.
""";
private readonly VisualStudioExtensibility extensibility;
private string sampleText = LoremIpsumText;
private bool manualUpdate = false;
/// <summary>
/// Initializes a new instance of the <see cref="MyToolWindowData" /> class.
/// </summary>
/// <param name="extensibility">
/// Extensibility object instance.
/// </param>
public MyToolWindowData(VisualStudioExtensibility extensibility)
{
this.extensibility = Requires.NotNull(extensibility, nameof(extensibility));
this.UpdateCommand = new AsyncCommand(this.UpdateAsync);
}
/// <summary>
/// Gets the async command used to show a message prompt.
/// </summary>
[DataMember]
public IAsyncCommand UpdateCommand
{
get;
}
/// <summary>
/// Gets or sets a value indicating whether the user must
/// click the "Update" button to refresh the sample text.
/// </summary>
[DataMember]
public bool ManualUpdate
{
get => this.manualUpdate;
set => this.SetProperty(ref this.manualUpdate, value);
}
/// <summary>
/// Gets or sets the message to display in the message prompt.
/// </summary>
[DataMember]
public string SampleText
{
get => this.sampleText;
set => this.SetProperty(ref this.sampleText, value);
}
/// <summary>
/// Initializes the current instance of <see cref="MyToolWindowData"/>.
/// </summary>
/// <param name="cancellationToken">Cancellation token to monitor.</param>
/// <returns>Task indicating completion of initialization.</returns>
public Task InitializeAsync(CancellationToken cancellationToken)
{
return this.InitializeSettingsAsync(cancellationToken);
}
#pragma warning disable VSEXTPREVIEW_SETTINGS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
private async Task InitializeSettingsAsync(CancellationToken cancellationToken)
{
await this.extensibility.Settings().SubscribeAsync(
[SettingDefinitions.SettingsSampleCategory],
cancellationToken,
values =>
{
if (values.TryGetValue(SettingDefinitions.AutoUpdateSetting.FullId, out ISettingValue? autoUpdateValue))
{
this.ManualUpdate = !autoUpdateValue.Value<bool>();
}
if (!this.ManualUpdate)
{
this.UpdateSampleTextFromSettings(values);
}
});
}
private async Task UpdateAsync(object? commandParameter, CancellationToken cancellationToken)
{
SettingValues values = await this.extensibility.Settings().ReadEffectiveValuesAsync(
[SettingDefinitions.SettingsSampleCategory],
cancellationToken);
this.UpdateSampleTextFromSettings(values);
}
private void UpdateSampleTextFromSettings(SettingValues values)
{
string text = LoremIpsumText;
if (values.TryGetValue(SettingDefinitions.TextLengthSetting.FullId, out ISettingValue? textLengthValue))
{
int length = textLengthValue.Value<int>();
text = LoremIpsumText[..Math.Min(length, LoremIpsumText.Length)];
}
if (values.TryGetValue(SettingDefinitions.QuoteStyleSetting.FullId, out ISettingValue? quoteStyleValue))
{
this.SampleText = quoteStyleValue.Value<string>() switch
{
"single" => $"'{text}'",
"double" => $"\"{text}\"",
_ => text,
};
}
}
#pragma warning restore VSEXTPREVIEW_SETTINGS // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
}

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

@ -0,0 +1,83 @@
---
title: Extension Settings Sample reference
description: A reference sample for extensions settings
date: 2024-08-20
---
# Walkthrough: Extension Settings Sample
This is a simple extension that shows how settings can be added to Visual Studio and read to configure the behavior of a tool window.
## Tool window definition
See the [ToolWindowSample](../ToolWindowSample/README.md) for more information on defining the tool window.
## Setting definitions
The extension contains a [code file](./SettingDefinitions.cs) that defines three settings and a parent category to contain them. Each setting and the category starts with the `VisualStudioContribution` class attribute which makes it available to Visual Studio:
```csharp
[VisualStudioContribution]
internal static SettingCategory SettingsSampleCategory { get; } = new("settingsSample", "%SettingsSample.Settings.Category.DisplayName%")
{
Description = "%SettingsSample.Settings.Category.Description%",
};
```
```csharp
[VisualStudioContribution]
internal static Setting.Boolean AutoUpdateSetting { get; } = new("autoUpdate", "%SettingsSample.Settings.AutoUpdate.DisplayName%", SettingsSampleCategory, defaultValue: true)
{
Description = "%SettingsSample.Settings.AutoUpdate.Description%",
};
The `SettingCategory` and `Setting.Boolean` properties define information about the settings that is available to Visual Studio even before the extension is loaded.
In `MyToolWindowData`, a subscription is created to read and monitor value changes for all the settings in the `SettingSampleCategory`:
```csharp
public MyToolWindowData(VisualStudioExtensibility extensibility)
{
...
this.InitializeSettingsAsync(extensibility).Forget();
}
private async Task InitializeSettingsAsync(VisualStudioExtensibility extensibility)
{
await extensibility.Settings().SubscribeAsync(
[SettingDefinitions.SettingsSampleCategory],
CancellationToken.None,
values => {...});
}
```
## Usage
Once deployed, the "Sample Text Tool Window" command can be used to show the "Settings Sample Tool Window" in the document well.
### Changing the TextLengthSetting
Setting values are stored in json files in well-known locations. After deploying the extension:
* Open the "Sample Text Tool Window": Tools -> Sample Text Tool Window
* Open the extension settings json file: Extensions -> Extension Settings (experimental) -> User Scope (current install)
The `extensibility.settings.json` file will open in an editor. To change the textLength setting, add a value to the file to override
the default:
```json
/* Visual Studio Settings File */
{
"settingsSample.textLength": 150
}
```
The string key is the `FullId` property of the `TextLengthSetting` property defined in [SettingDefinitions](./SettingDefinitions.cs). It is formed by the id of the category and the id of the setting.
Each time you change the value and save the file, the sample text in the tool window will update.
## Current Limitations
The settings API is currently experimental, and has several limitations:
* An extension can only read or write settings from itself or other extensions. Core Visual Studio settings are not available.
* There is no UI for extension settings. They can only be changed by using the json files available in the Extensions -> Extension Settings (experimental) menu.

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

@ -0,0 +1,43 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace SettingsSample;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Settings;
#pragma warning disable VSEXTPREVIEW_SETTINGS // The settings API is currently in preview and marked as experimental
internal static class SettingDefinitions
{
[VisualStudioContribution]
internal static SettingCategory SettingsSampleCategory { get; } = new("settingsSample", "%SettingsSample.Settings.Category.DisplayName%")
{
Description = "%SettingsSample.Settings.Category.Description%",
};
[VisualStudioContribution]
internal static Setting.Boolean AutoUpdateSetting { get; } = new("autoUpdate", "%SettingsSample.Settings.AutoUpdate.DisplayName%", SettingsSampleCategory, defaultValue: true)
{
Description = "%SettingsSample.Settings.AutoUpdate.Description%",
};
[VisualStudioContribution]
internal static Setting.Integer TextLengthSetting { get; } = new("textLength", "%SettingsSample.Settings.TextLength.DisplayName%", SettingsSampleCategory, defaultValue: 10)
{
Description = "%SettingsSample.Settings.TextLength.Description%",
Minimum = 1,
Maximum = MyToolWindowData.LoremIpsumText.Length,
};
[VisualStudioContribution]
internal static Setting.Enum QuoteStyleSetting { get; } = new(
"quoteStyle",
"%SettingsSample.Settings.QuoteStyle.DisplayName%",
SettingsSampleCategory,
[new EnumSettingEntry("none", "%SettingsSample.Settings.QuoteStyle.None%"), new EnumSettingEntry("single", "%SettingsSample.Settings.QuoteStyle.Single%"), new EnumSettingEntry("double", "%SettingsSample.Settings.QuoteStyle.Double%")],
defaultValue: "double")
{
Description = "%SettingsSample.Settings.QuoteStyle.Description%",
};
}

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

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>12</LangVersion>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Sdk" Version="17.11.40261" />
<PackageReference Include="Microsoft.VisualStudio.Extensibility.Build" Version="17.11.40261" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="MyToolWindowControl.xaml" />
<Page Remove="MyToolWindowControl.xaml" />
</ItemGroup>
</Project>

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

@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace SettingsSample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.VisualStudio.Extensibility;
/// <summary>
/// Extension entry point for the SettingsSample.
/// </summary>
[VisualStudioContribution]
public class SettingsSampleExtension : Extension
{
/// <inheritdoc/>
public override ExtensionConfiguration ExtensionConfiguration => new()
{
Metadata = new(
id: "SettingsSampleExtension.4ca466cb-dc8d-4216-8323-b5c45f8e0da5",
version: this.ExtensionAssemblyVersion,
publisherName: "Microsoft",
displayName: "Settings Sample Extension",
description: "Sample extension demonstrating settings"),
};
/// <inheritdoc/>
protected override void InitializeServices(IServiceCollection serviceCollection)
{
base.InitializeServices(serviceCollection);
}
}