Options Pages

Options pages allow users to configure your extension through the familiar Tools > Options dialog. Settings can be persisted automatically by Visual Studio.

Creating Options Pages

With Community Toolkit

The toolkit provides a simple base class:

public class GeneralOptions : BaseOptionModel<GeneralOptions>
{
    [Category("General")]
    [DisplayName("Enable Feature")]
    [Description("Enables the main feature of this extension.")]
    [DefaultValue(true)]
    public bool EnableFeature { get; set; } = true;

    [Category("General")]
    [DisplayName("Max Items")]
    [Description("Maximum number of items to display.")]
    [DefaultValue(10)]
    public int MaxItems { get; set; } = 10;

    [Category("Appearance")]
    [DisplayName("Show Notifications")]
    [Description("Show notification popups for important events.")]
    [DefaultValue(true)]
    public bool ShowNotifications { get; set; } = true;
}

Register in your package:

[ProvideOptionPage(typeof(OptionsProvider.GeneralOptionsPage), "My Extension", "General", 0, 0, true)]
public sealed class MyPackage : ToolkitPackage { }

public class OptionsProvider
{
    [ComVisible(true)]
    public class GeneralOptionsPage : BaseOptionPage<GeneralOptions> { }
}

Access settings anywhere:

// Read settings
var options = await GeneralOptions.GetLiveInstanceAsync();
if (options.EnableFeature)
{
    // Feature is enabled
}

// Save settings
var options = await GeneralOptions.GetLiveInstanceAsync();
options.MaxItems = 20;
await options.SaveAsync();

Traditional Approach

Without the Community Toolkit:

[Guid("your-options-page-guid")]
public class GeneralOptionsPage : DialogPage
{
    [Category("General")]
    [DisplayName("Enable Feature")]
    [Description("Enables the main feature of this extension.")]
    public bool EnableFeature { get; set; } = true;

    [Category("General")]
    [DisplayName("Max Items")]
    [Description("Maximum number of items to display.")]
    public int MaxItems { get; set; } = 10;
}

Access settings:

var page = (GeneralOptionsPage)Package.GetDialogPage(typeof(GeneralOptionsPage));
var enabled = page.EnableFeature;

Property Types

Options pages support various property types:

Basic Types

public bool BooleanOption { get; set; }
public int IntegerOption { get; set; }
public string StringOption { get; set; }
public double DoubleOption { get; set; }

Enums

public enum LogLevel
{
    None,
    Error,
    Warning,
    Info,
    Debug
}

[Category("Logging")]
[DisplayName("Log Level")]
public LogLevel LoggingLevel { get; set; } = LogLevel.Warning;

File/Folder Paths

[Category("Paths")]
[DisplayName("Output Directory")]
[Editor(typeof(FolderBrowserEditor), typeof(UITypeEditor))]
public string OutputDirectory { get; set; }

[Category("Paths")]
[DisplayName("Config File")]
[Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
public string ConfigFile { get; set; }
Note

The file/folder editors require System.Windows.Forms.Design reference.

Custom UI Pages

For complex settings, create a custom WPF UI:

[Guid("your-custom-page-guid")]
public class CustomOptionsPage : UIElementDialogPage
{
    private CustomOptionsControl _control;

    protected override UIElement Child
    {
        get { return _control ?? (_control = new CustomOptionsControl()); }
    }

    protected override void OnActivate(CancelEventArgs e)
    {
        base.OnActivate(e);
        _control.LoadSettings(Settings.Default);
    }

    protected override void OnApply(PageApplyEventArgs e)
    {
        _control.SaveSettings(Settings.Default);
        base.OnApply(e);
    }
}

The WPF control:

<UserControl x:Class="MyExtension.CustomOptionsControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel Margin="10">
        <CheckBox x:Name="EnableFeatureCheckbox" Content="Enable Feature" Margin="0,0,0,10"/>
        <Label Content="Maximum Items:"/>
        <Slider x:Name="MaxItemsSlider" Minimum="1" Maximum="100" TickFrequency="1"/>
        <TextBlock Text="{Binding ElementName=MaxItemsSlider, Path=Value, StringFormat='{}{0:N0}'}"/>
    </StackPanel>
</UserControl>

Nested Categories

Create hierarchical options:

[ProvideOptionPage(typeof(GeneralOptionsPage), "My Extension", "General", 0, 0, true)]
[ProvideOptionPage(typeof(AdvancedOptionsPage), "My Extension", "Advanced", 0, 0, true)]
[ProvideOptionPage(typeof(FormattingOptionsPage), "My Extension\\Editor", "Formatting", 0, 0, true)]
public sealed class MyPackage : ToolkitPackage { }

This creates:

My Extension
├── General
├── Advanced
└── Editor
    └── Formatting

Settings Change Notification

React to settings changes:

public class GeneralOptions : BaseOptionModel<GeneralOptions>
{
    public bool EnableFeature { get; set; } = true;

    // Called when settings are saved
    public override void Save()
    {
        base.Save();
        OnSettingsChanged?.Invoke(this, EventArgs.Empty);
    }

    public event EventHandler OnSettingsChanged;
}

// Subscribe to changes
GeneralOptions.Instance.OnSettingsChanged += (s, e) =>
{
    // Settings changed, update UI or behavior
};

Validation

Validate settings before saving:

public class GeneralOptions : BaseOptionModel<GeneralOptions>
{
    private int _maxItems = 10;

    [Category("General")]
    [DisplayName("Max Items")]
    public int MaxItems
    {
        get => _maxItems;
        set
        {
            if (value < 1 || value > 1000)
                throw new ArgumentOutOfRangeException(nameof(value), "Max Items must be between 1 and 1000.");
            _maxItems = value;
        }
    }
}

Import/Export Settings

Let users export and import settings:

[ProvideProfile(typeof(GeneralOptionsPage), "My Extension", "General Settings", 100, 100, true)]
public sealed class MyPackage : ToolkitPackage { }

Users can then export settings via Tools > Import and Export Settings.

Complete Example

// Options model
public class MyExtensionOptions : BaseOptionModel<MyExtensionOptions>
{
    // General settings
    [Category("General")]
    [DisplayName("Enable Feature")]
    [Description("Enables the main feature of this extension.")]
    [DefaultValue(true)]
    public bool EnableFeature { get; set; } = true;

    [Category("General")]
    [DisplayName("Auto-save")]
    [Description("Automatically save changes.")]
    [DefaultValue(true)]
    public bool AutoSave { get; set; } = true;

    // Editor settings
    [Category("Editor")]
    [DisplayName("Font Size")]
    [Description("Font size for the custom editor.")]
    [DefaultValue(12)]
    public int FontSize { get; set; } = 12;

    [Category("Editor")]
    [DisplayName("Theme")]
    public EditorTheme Theme { get; set; } = EditorTheme.Dark;

    // Advanced settings
    [Category("Advanced")]
    [DisplayName("Debug Mode")]
    [Description("Enable debug logging (affects performance).")]
    [DefaultValue(false)]
    public bool DebugMode { get; set; } = false;
}

public enum EditorTheme
{
    Light,
    Dark,
    System
}

// Package registration
[ProvideOptionPage(typeof(OptionsProvider.GeneralOptionsPage), "My Extension", "General", 0, 0, true)]
[ProvideOptionPage(typeof(OptionsProvider.EditorOptionsPage), "My Extension", "Editor", 0, 0, true)]
[ProvideOptionPage(typeof(OptionsProvider.AdvancedOptionsPage), "My Extension", "Advanced", 0, 0, true)]
public sealed class MyPackage : ToolkitPackage { }

public class OptionsProvider
{
    [ComVisible(true)]
    public class GeneralOptionsPage : BaseOptionPage<MyExtensionOptions> { }

    [ComVisible(true)]
    public class EditorOptionsPage : BaseOptionPage<MyExtensionOptions> { }

    [ComVisible(true)]
    public class AdvancedOptionsPage : BaseOptionPage<MyExtensionOptions> { }
}

// Usage in commands
[Command(PackageIds.MyCommand)]
internal sealed class MyCommand : BaseCommand<MyCommand>
{
    protected override async Task ExecuteAsync(OleMenuCmdEventArgs e)
    {
        var options = await MyExtensionOptions.GetLiveInstanceAsync();

        if (!options.EnableFeature)
        {
            await VS.MessageBox.ShowWarningAsync("Feature Disabled",
                "Enable this feature in Tools > Options > My Extension > General");
            return;
        }

        // Use options.FontSize, options.Theme, etc.
    }
}

Next Steps

Learn about Async Services for background loading patterns.