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.