Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/DevToys-app/DevToys/llms.txt

Use this file to discover all available pages before exploring further.

DevToys provides a first-class settings persistence API through ISettingsProvider. Any value your tool needs to survive across sessions — a preferred encoding format, an output toggle, a remembered file path — can be stored with a single method call and retrieved just as easily. Settings are stored locally per-user and survive app updates.

Injecting ISettingsProvider

ISettingsProvider is available as a MEF import. Add an [Import] field to your tool class and MEF will inject it automatically at composition time:
using System.ComponentModel.Composition;
using DevToys.Api;

[Export(typeof(IGuiTool))]
[Name("MyExtension.MyTool")]
// ... other attributes ...
internal sealed class MyTool : IGuiTool
{
    [Import]
    private ISettingsProvider _settingsProvider = default!;

    // ...
}
Use default! to suppress the nullable warning — MEF guarantees the field is populated before any public member is accessed.

Defining a Setting

A setting is described by a SettingDefinition<T> value. Declare it as a static readonly field on your tool class so it is created only once. The name parameter must be unique across all extensions; use a reverse-DNS style prefix to avoid collisions.
// Boolean toggle — default is true
private static readonly SettingDefinition<bool> EncodingUtf8Setting
    = new(name: "MyDevToysExtension.MyTool.EncodingUtf8", defaultValue: true);

// Enum — default is OutputFormat.Hex
private static readonly SettingDefinition<OutputFormat> OutputFormatSetting
    = new(name: "MyDevToysExtension.MyTool.OutputFormat", defaultValue: OutputFormat.Hex);
SettingDefinition<T> has two constructor overloads:
// Simple — uses built-in serialization for primitives and enums
public SettingDefinition(string name, T defaultValue);

// With custom serializers — required for complex types
public SettingDefinition(
    string name,
    T defaultValue,
    Func<T, string> serialize,
    Func<string, T> deserialize);
Setting names cannot contain the = character (reserved for the .ini file format used in portable installations) and are limited to 255 characters total — including the assembly prefix that SettingDefinition appends automatically.

Reading and Writing Settings

ISettingsProvider exposes three methods:
// Get the current value, or the default if never set.
T GetSetting<T>(SettingDefinition<T> settingDefinition);

// Persist a new value.
void SetSetting<T>(SettingDefinition<T> settingDefinition, T value);

// Reset to the default value defined in SettingDefinition<T>.
void ResetSetting<T>(SettingDefinition<T> settingDefinition);
Usage example:
// Read
bool isUtf8 = _settingsProvider.GetSetting(EncodingUtf8Setting);

// Write
_settingsProvider.SetSetting(EncodingUtf8Setting, false);

// Reset
_settingsProvider.ResetSetting(EncodingUtf8Setting);

Reacting to Setting Changes

Subscribe to ISettingsProvider.SettingChanged to be notified when any setting value changes. This is useful for keeping UI elements in sync when the user changes a preference from a settings panel.
_settingsProvider.SettingChanged += OnSettingChanged;

private void OnSettingChanged(object? sender, SettingChangedEventArgs e)
{
    if (e.SettingName == EncodingUtf8Setting.Name)
    {
        bool isUtf8 = _settingsProvider.GetSetting(EncodingUtf8Setting);
        _utf8Switch.On(isUtf8);
    }
}

Full Working Example

The following example shows a tool that remembers a user’s output format preference and updates its UI reactively when the setting changes.
using System.ComponentModel.Composition;
using DevToys.Api;
using static DevToys.Api.GUI;

internal enum OutputFormat { Hex, Base64, Utf8 }

[Export(typeof(IGuiTool))]
[Name("MyExtension.ByteInspector")]
[ToolDisplayInformation(
    IconFontName = "FluentSystemIcons",
    IconGlyph = '\uF4E3',
    GroupName = PredefinedCommonToolGroupNames.Converters,
    ResourceManagerAssemblyIdentifier = nameof(MyExtensionResourceAssemblyIdentifier),
    ResourceManagerBaseName = "MyDevToysExtension.Strings",
    ShortDisplayTitleResourceName = nameof(Strings.ShortDisplayTitle))]
internal sealed class ByteInspectorTool : IGuiTool
{
    // ── Setting definitions ───────────────────────────────────────────────────

    private static readonly SettingDefinition<OutputFormat> OutputFormatSetting
        = new(name: "MyDevToysExtension.ByteInspector.OutputFormat",
              defaultValue: OutputFormat.Hex);

    // ── MEF imports ───────────────────────────────────────────────────────────

    [Import]
    private ISettingsProvider _settingsProvider = default!;

    // ── UI elements ───────────────────────────────────────────────────────────

    private readonly IUIMultiLineTextInput _inputText
        = MultiLineTextInput("byte-inspector-input");

    private readonly IUIMultiLineTextInput _outputText
        = MultiLineTextInput("byte-inspector-output");

    private readonly IUISelectDropDownList _formatDropDown
        = SelectDropDownList("byte-inspector-format");

    // ── IGuiTool ──────────────────────────────────────────────────────────────

    public UIToolView View
    {
        get
        {
            // Restore persisted setting.
            OutputFormat savedFormat = _settingsProvider.GetSetting(OutputFormatSetting);

            // Listen for changes from other parts of the app.
            _settingsProvider.SettingChanged += OnSettingChanged;

            // Build the drop-down items and select the saved format by index.
            var items = new[]
            {
                Item("Hexadecimal", OutputFormat.Hex),
                Item("Base64",      OutputFormat.Base64),
                Item("UTF-8",       OutputFormat.Utf8),
            };
            int savedIndex = (int)savedFormat; // enum values map to indices 0, 1, 2

            _formatDropDown
                .WithItems(items)
                .Select(savedIndex)
                .OnItemSelected(OnOutputFormatSelected);

            return new(
                Stack()
                    .Vertical()
                    .MediumSpacing()
                    .WithChildren(
                        _formatDropDown.Title("Output format"),
                        _inputText.Title("Input").OnTextChanged(UpdateOutput),
                        _outputText.Title("Output").ReadOnly().CanCopyWhenEditable()));
        }
    }

    public void OnDataReceived(string dataTypeName, object? parsedData)
    {
        if (parsedData is string text)
            _inputText.Text(text);
    }

    // ── Event handlers ────────────────────────────────────────────────────────

    private void OnOutputFormatSelected(IUIDropDownListItem? item)
    {
        if (item?.Value is OutputFormat format)
        {
            _settingsProvider.SetSetting(OutputFormatSetting, format);
            UpdateOutput(_inputText.Text);
        }
    }

    private void OnSettingChanged(object? sender, SettingChangedEventArgs e)
    {
        if (e.SettingName == OutputFormatSetting.Name)
            UpdateOutput(_inputText.Text);
    }

    private void UpdateOutput(string input)
    {
        OutputFormat format = _settingsProvider.GetSetting(OutputFormatSetting);
        byte[] bytes = System.Text.Encoding.UTF8.GetBytes(input);

        string result = format switch
        {
            OutputFormat.Hex    => Convert.ToHexString(bytes),
            OutputFormat.Base64 => Convert.ToBase64String(bytes),
            OutputFormat.Utf8   => System.Text.Encoding.UTF8.GetString(bytes),
            _                   => string.Empty
        };

        _outputText.Text(result);
    }
}

Tips

Use reverse-DNS style names such as "com.mycompany.mytool.mysetting" or "MyCompany.MyTool.MySetting" to ensure your setting names do not collide with those from other extensions or from DevToys itself.
The SettingDefinition<T> constructor automatically prepends the calling assembly name to your base name, so "MyTool.OutputFormat" becomes "MyDevToysExtension.MyTool.OutputFormat" in storage. You do not need to include the assembly name manually — but using a descriptive prefix is still good practice.
Settings are stored locally per-user. On Windows, they are kept in the user’s app data folder; on macOS and Linux, they follow the standard per-user configuration directory conventions. Settings are not synced across devices.

Build docs developers (and LLMs) love