Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
The ordinary settings API is stable. External settings providers are experimental, require opting in to VSEXTPREVIEW_SETTINGS_EXTERNAL, and may change in a later SDK. See Experimental APIs ↗ for guidance on opting in. Ordinary settings do not require VSEXTPREVIEW_SETTINGS.
Back settings with an external store
An ExternalSettingsProvider supplies values from a store your extension owns instead of using the normal settings store. Its ExternalSettingsProviderConfiguration declares a category and externally backed settings that Visual Studio can display without loading the extension merely to discover their metadata.
The ExternalSettingsSample ↗ has one Greeting setting backed by an in-memory store. Its complete provider demonstrates the VSEXTPREVIEW_SETTINGS_EXTERNAL opt-in in that file:
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#pragma warning disable VSEXTPREVIEW_SETTINGS_EXTERNAL // External settings providers are experimental in this SDK.
namespace ExternalSettingsSample;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.Extensibility;
using Microsoft.VisualStudio.Extensibility.Settings;
[VisualStudioContribution]
internal sealed class SampleExternalSettingsProvider : ExternalSettingsProvider
{
private const string GreetingId = "greeting";
private const string InvalidGreetingMessage = "Enter a greeting of 1 to 24 non-whitespace characters.";
private readonly SampleSettingsStore store;
public SampleExternalSettingsProvider(SampleSettingsStore store)
{
this.store = store;
this.store.Changed += this.OnStoreChanged;
}
[VisualStudioContribution]
internal static SettingCategory Category { get; } = new("externalSettingsSample", "%ExternalSettingsSample.Category.DisplayName%")
{
Description = "%ExternalSettingsSample.Category.Description%",
};
/// <inheritdoc/>
protected override ExternalSettingsProviderConfiguration ExternalSettingsProviderConfiguration => new(
"memoryStore",
"%ExternalSettingsSample.Provider.DisplayName%",
Category,
[
new ExternalSetting.String(GreetingId, "%ExternalSettingsSample.Greeting.DisplayName%")
{
Description = "%ExternalSettingsSample.Greeting.Description%",
GetValue = (identifier, cancellationToken) =>
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(ExternalSettingOperationResult<string>.Success(this.store.Greeting));
},
SetValue = (value, identifier, cancellationToken) =>
{
cancellationToken.ThrowIfCancellationRequested();
if (!IsValid(value))
{
return Task.FromResult(ExternalSettingOperationResult.Failure(InvalidGreetingMessage, ExternalSettingsErrorScope.SingleSettingOnly, isTransient: false));
}
this.store.SetGreeting(value);
return Task.FromResult(ExternalSettingOperationResult.Success());
},
Validate = (value, identifier, cancellationToken) =>
{
cancellationToken.ThrowIfCancellationRequested();
IReadOnlyList<SettingMessage> messages = IsValid(value)
? []
: [new SettingMessage(InvalidGreetingMessage) { Severity = SettingMessageSeverity.Error }];
return Task.FromResult(messages);
},
},
])
{
Description = "%ExternalSettingsSample.Provider.Description%",
BackingStoreDescription = "%ExternalSettingsSample.Provider.BackingStoreDescription%",
};
/// <inheritdoc/>
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
this.store.Changed -= this.OnStoreChanged;
}
base.Dispose(isDisposing);
}
private static bool IsValid(string value) => !string.IsNullOrWhiteSpace(value) && value.Length <= 24;
private void OnStoreChanged() => this.NotifySettingValuesChanged([GreetingId]);
}
The provider uses four callbacks and notifications to keep the settings UI and store consistent:
GetValuereads the current greeting and returnsExternalSettingOperationResult<string>.Success.Validatereturns aSettingMessagewith error severity for blank or overlong input.SetValuealso rejects invalid writes withExternalSettingOperationResult.Failure; it never reports an invalid write as successful.- A successful
SetValuechanges the backing store. Changes made outside the settings UI raise the store'sChangedevent, which callsNotifySettingValuesChangedso Visual Studio refreshes the greeting. Disposeremoves the store event handler when the provider is no longer needed.
A provider must return explicit failures for unavailable reads and writes, and optionally notify Visual Studio when its backing values change.