VisualStudio.Extensibility 设置

设置允许扩展作者定义最终用户可配置的值以影响扩展的行为。

扩展可以读取、写入或观察设置值。 设置在类别层次结构中组织,并提供对文档和验证的丰富支持。

定义设置

若要开始,请按照 创建您的第一个扩展 教程。

创建扩展后,可以将设置定义添加到扩展中的任何类:

#pragma warning disable VSEXTPREVIEW_SETTINGS // The settings API is currently in preview and marked as experimental

private static class SettingDefinitions
{
    [VisualStudioContribution]
    private static SettingCategory MyCategory { get; } = new("myCategory", "My Category");

    [VisualStudioContribution]
    private static Setting.Boolean MySetting { get; } = new("mySetting", "My Setting", MyCategory, defaultValue: false);
}

上面的代码定义名为 mySettingmyCategory 类别下的 true/false(布尔值)设置,默认值为 false。 请注意,设置和类别都具有 VisualStudioContribution 属性。

向用户显示设置和类别的显示名称时,建议使用本地化字符串(请参阅 本地化文档):

[VisualStudioContribution]
private static SettingCategory MyCategory { get; } = new("myCategory", "%MyExtension.SettingDefinitions.MyCategory%"){
    Description = "%MyExtension.SettingDefinitions.MyCategory.Description%",
    GenerateObserverClass = true,
};

[VisualStudioContribution]
private static Setting.Boolean MySetting { get; } = new("mySetting", "%MyExtension.SettingDefinitions.MySetting%", MyCategory, defaultValue: false){
    Description = "%MyExtension.SettingDefinitions.MySetting.Description%",
};

还可以嵌套类别:

[VisualStudioContribution]
private static SettingCategory ParentCategory { get; } = new("parentCategory", "Parent Category");

[VisualStudioContribution]
private static SettingCategory ChildCategory { get; } = new("childCategory", "Child Category", ParentCategory);

[VisualStudioContribution]
private static Setting.Boolean MySetting { get; } = new("mySetting", "My Setting", ChildCategory, defaultValue: false);

读取和监视设置值

GenerateObserverClass 设置为 SettingCategory 定义中的 true 时,将生成观察程序类。 观察器可用于读取和监视类别中的设置。 可以通过在 ExtensionInitializeServices 方法中调用 serviceCollection.AddSettingsObservers();,使观察程序可用于依赖项注入:

    protected override void InitializeServices(IServiceCollection serviceCollection)
    {
        serviceCollection.AddSettingsObservers();
        base.InitializeServices(serviceCollection);
    }

然后,可以从通过依赖项注入创建的任何组件(如命令或工具窗口)使用观察程序:

public MyToolWindow(Settings.MyCategoryObserver settingsObserver)
{
    this.settingsObserver = settingsObserver;
}

private async Task DoSomethingAsync()
{
    var settingsSnapshot = await this.settingsObserver.GetSnapshotAsync(cancellationToken);
    bool mySetting = settingsSnapshot.MySetting.ValueOrDefault(defaultValue: true);
}

MyCategoryObserver 类在扩展命名空间的 Settings 子命名空间下生成。

当类别中的任何值发生更改时,也可以使用观察程序引发事件。

public MyToolWindow(Settings.MyCategoryObserver settingsObserver)
{
    settingsObserver.Changed += this.SettingsObserver_ChangedAsync;
}

private async Task SettingsObserver_ChangedAsync(Settings.MyCategorySnapshot settingsSnapshot)
{
    this.MySetting = settingsSnapshot.MySetting.ValueOrDefault(defaultValue: true);
    ...
}

Changed 事件处理程序始终至少被调用一次,且使用当前设置值,因此无需读取初始值。

设定一个场景

在某些情况下,扩展可能需要更改设置的值。 可以通过批量写入操作来实现,该操作允许同时更新多个设置。

var writeResult = await Extensibility.Settings().WriteAsync(
    batch =>
    {
        batch.WriteSetting(ExtensionEntrypoint.MySetting1, value: true);
        batch.WriteSetting(ExtensionEntrypoint.MySetting2, value: "foo");
    },
    description: "Updating the settings' value to true and `foo`",
    cancellationToken);

传递给 WriteAsync 调用的说明应本地化。

读取设置

可以从扩展中的任何位置访问设置的值。 虽然最好使用观察程序持续监视设置值,但可能会出现扩展不需要持续监视整个设置类别的情况。 下面的代码演示如何从命令的处理程序读取上面定义的设置。

public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
    var result = await Extensibility.Settings().ReadEffectiveValueAsync(SettingDefinitions.MySetting, cancellationToken);
    bool value = result.ValueOrDefault(defaultValue: false);
    ...
}

请注意,ReadEffectiveValueAsync 返回的是下列之一的 SettingValue<>

  • 设置的当前值
  • 为设置声明的默认值(如果从未为设置分配值)
  • 读取期间发生的错误。

通常,你会利用 ValueOrDefault() 方法来获取检索到的设置值或调用时提供的回退值。

还可以利用 ReadEffectiveValuesAsync 方法通过单个调用读取多个设置。

public override async Task ExecuteCommandAsync(IClientContext context, CancellationToken cancellationToken)
{
    var results = await Extensibility.Settings().ReadEffectiveValuesAsync(
        [ExtensionEntrypoint.MySetting1, ExtensionEntrypoint.MySetting2],
        cancellationToken);
    var value = results.ValueOrDefault(ExtensionEntrypoint.MySetting1, defaultValue: false);
    ...
}

监视某个设置

在不使用观察者的情况下,您也可以监视设置的值:

IDisposable disposeToEndSubscription =
    await Extensibility.Settings().SubscribeAsync(
        ExtensionEntrypoint.MySetting,
        cancellationToken,
        changeHandler: result =>
        {
            var value = result.ValueOrDefault(defaultValue: false);
            ...
        });

SubscribeAsync 更改处理程序保证确保至少用设置的当前值调用一次。

设置值和设置元数据的类型

设置不限于布尔值类型。 可以使用多种类型定义它们,其中包括:

  • Setting.Boolean
  • Setting.Decimal(对于浮点值)
  • Setting.Enum(对于多个选项,每个选项都由本地化字符串表示)
  • Setting.FormattedString(日期、时间、IP 地址、电子邮件、URI、文件和目录路径)
  • Setting.Integer
  • Setting.String
  • Setting.EnumArray
  • Setting.FormattedStringArray
  • Setting.StringArray
  • Setting.ObjectArray(一系列项,每个项都具有多个类型属性)

每种类型都允许定义适当的验证规则和其他元数据,例如向最终用户显示文档。 这是一个更完整的字符串类型设置定义的示例:

[VisualStudioContribution]
private static Setting.String MyStringSetting { get; } = new(
    "myStringSetting",
    "%MyExtension.SettingDefinitions.MyStringSetting%",
    MyCategory,
    defaultValue: "")
{
    MaxStringLength = 100,
    Description = "%MyExtension.SettingDefinitions.MyStringSetting.Description%",
};

这是枚举设置的定义示例:

[VisualStudioContribution]
private static Setting.Enum MyEnumSetting { get; } = new(
    "myEnumSetting",
    "%MyExtension.SettingDefinitions.MyEnumSetting%",
    MyCategory,
    [
        new("true", "%MyExtension.SettingDefinitions.MyEnumSetting.True%"),
        new("false", "%MyExtension.SettingDefinitions.MyEnumSetting.False%"),
        new("disabled", "%MyExtension.SettingDefinitions.MyEnumSetting.Disabled%"),
    ],
    defaultValue: "disabled")
{
    Description = "%MyExtension.SettingDefinitions.MyEnumSetting.Description%",
};

有关详细信息,请参阅 设置 API 文档