Background and motivation
This proposal adds a consolidated Application.SystemVisualSettings snapshot plus one change
notification, covering the Windows accent color, the Accessibility Text size factor, high
contrast, client-area animation, keyboard-cue visibility, and focus-border metrics.
This design evolved during implementation. It originally started as two narrow, single-purpose
members — Application.GetWindowsAccentColor() and Application.SystemTextSize /
SystemTextSizeChanged (plus a mirrored Form.SystemTextSizeChanged). While implementing those,
it became clear that Windows reports visual/accessibility changes through four different messages
(WM_SETTINGCHANGE, WM_DWMCOLORIZATIONCOLORCHANGED, WM_THEMECHANGED, WM_SYSCOLORCHANGE), that
several other related settings needed the same treatment, and that a static-event-only design risks
the classic leak of rooting every subscriber for the application's lifetime. The proposal below is
the result: one immutable settings snapshot and one leak-free notification path.
SystemVisualSettings.TextScaleFactor represents Settings > Accessibility > Text size, not
display DPI scaling. DPI remains covered by HighDpiMode, WM_DPICHANGED, and the existing DPI
events.
Note also: Animation can also be controlled via the Windows A11Y settings, and using VisualStylesMode controlled styles are including a bunch of different Animations:
API proposal
namespace System.Windows.Forms;
/// <summary>
/// Represents an immutable snapshot of visual and accessibility settings that Windows owns.
/// </summary>
public sealed class SystemVisualSettings
{
/// <summary>Gets the user's current Windows accent color.</summary>
public Color AccentColor { get; }
/// <summary>
/// Gets the Windows Accessibility text-scale factor (1.0-2.25), independent of display DPI.
/// </summary>
public float TextScaleFactor { get; }
/// <summary>Gets whether the user has enabled Windows high-contrast mode.</summary>
public bool HighContrastEnabled { get; }
/// <summary>Gets whether Windows enables client-area animations.</summary>
public bool ClientAreaAnimationEnabled { get; }
/// <summary>Gets whether the system default displays keyboard cues.</summary>
public bool KeyboardCuesVisible { get; }
/// <summary>Gets the Windows focus-border width and height, in pixels.</summary>
public Size FocusBorderMetrics { get; }
}
[Flags]
public enum SystemVisualSettingsCategories
{
None = 0,
AccentColor = 1 << 0,
TextScale = 1 << 1,
HighContrast = 1 << 2,
Animations = 1 << 3,
KeyboardCues = 1 << 4,
FocusMetrics = 1 << 5
}
public class SystemVisualSettingsChangedEventArgs : EventArgs
{
public SystemVisualSettings OldSettings { get; }
public SystemVisualSettings NewSettings { get; }
public SystemVisualSettingsCategories Changed { get; }
}
public delegate void SystemVisualSettingsChangedEventHandler(object? sender, SystemVisualSettingsChangedEventArgs e);
public sealed partial class Application
{
/// <summary>Gets the current Windows visual and accessibility settings snapshot.</summary>
public static SystemVisualSettings SystemVisualSettings { get; }
/// <summary>
/// Occurs when Windows visual or accessibility settings change. Intended for
/// application-lifetime consumers (theming engines, services); subscribers must
/// unsubscribe if their lifetime is shorter than the application. Controls and forms
/// should use <see cref="Control.SystemVisualSettingsChanged"/> instead.
/// </summary>
public static event SystemVisualSettingsChangedEventHandler? SystemVisualSettingsChanged;
}
public partial class Control
{
/// <summary>
/// Occurs when Windows visual or accessibility settings change. Unlike
/// <see cref="Application.SystemVisualSettingsChanged"/>, this instance event requires no
/// unsubscription — it follows the control's own lifetime.
/// </summary>
public event SystemVisualSettingsChangedEventHandler? SystemVisualSettingsChanged;
/// <summary>Raises the <see cref="SystemVisualSettingsChanged"/> event.</summary>
protected virtual void OnSystemVisualSettingsChanged(SystemVisualSettingsChangedEventArgs e);
}
Usage
// App-level, e.g. a theming service:
Color accent = Application.SystemVisualSettings.AccentColor;
Application.SystemVisualSettingsChanged += (s, e) =>
{
if (e.Changed.HasFlag(SystemVisualSettingsCategories.AccentColor))
{
// Re-theme using e.NewSettings.AccentColor.
}
};
// Control/Form-level, no unsubscription needed:
public sealed class MainForm : Form
{
private const float BaseFontSize = 9.0f;
protected override void OnSystemVisualSettingsChanged(SystemVisualSettingsChangedEventArgs e)
{
base.OnSystemVisualSettingsChanged(e);
if (e.Changed.HasFlag(SystemVisualSettingsCategories.TextScale))
{
Font = new Font(Font.FontFamily, BaseFontSize * e.NewSettings.TextScaleFactor, Font.Style, Font.Unit);
PerformLayout();
}
}
}
Applications that replace fonts repeatedly should retain and dispose the font instances they own.
The framework deliberately does not perform automatic font scaling or relayout — font and layout
policies are application-specific.
Rationale and alternative designs
- One snapshot type instead of N separate getters/events: accent color, text scale, high
contrast, animations, keyboard cues, and focus-border metrics all arrive through the same set of
Windows messages; one type with a flags-based Changed lets consumers early-out cheaply instead of
wiring up several independent events.
Control instance event/virtual instead of relying only on the static Application event: a
static-only design requires every subscribing control/form to unsubscribe or leak; the Control
cascade (parent → children, modeled on the existing OnSystemColorsChanged pattern) is scoped to
the control's own lifetime and needs no subscription management.
- Flags enum for
Changed instead of separate events per category: keeps the notification
surface to one event while still letting consumers react selectively.
- Considered and rejected: keeping the original narrow
GetWindowsAccentColor() method and a
separate SystemTextSize/SystemTextSizeChanged pair — this would have required a second,
near-identical mechanism per additional setting (high contrast, animations, etc.) and duplicated
the message-normalization logic.
Behavior
- The snapshot is refreshed by normalizing
WM_SETTINGCHANGE, WM_DWMCOLORIZATIONCOLORCHANGED,
WM_THEMECHANGED, and WM_SYSCOLORCHANGE; SystemVisualSettingsChanged is raised once per actual
transition, with Changed reporting only the categories that differ from the previous snapshot.
- Applications opt in simply by subscribing to an event or overriding
Control.OnSystemVisualSettingsChanged; existing applications do not change behavior.
Will this feature affect UI controls?
The API does not automatically change existing controls. It provides the shared settings snapshot
and change notification that renderers, applications, and follow-up control APIs use to update their
own rendering, measurement, and layout.
Designer impact: Control.SystemVisualSettingsChanged appears as a standard event. There are no
new serialization requirements.
Accessibility impact: Applications and controls can react when the user changes Windows
accent color, high contrast, text size, animation, or keyboard-cue settings while running.
Localization: No user-visible strings are introduced.
I'll attach an animated GIF demo later.
Background and motivation
This proposal adds a consolidated
Application.SystemVisualSettingssnapshot plus one changenotification, covering the Windows accent color, the Accessibility Text size factor, high
contrast, client-area animation, keyboard-cue visibility, and focus-border metrics.
This design evolved during implementation. It originally started as two narrow, single-purpose
members —
Application.GetWindowsAccentColor()andApplication.SystemTextSize/SystemTextSizeChanged(plus a mirroredForm.SystemTextSizeChanged). While implementing those,it became clear that Windows reports visual/accessibility changes through four different messages
(
WM_SETTINGCHANGE,WM_DWMCOLORIZATIONCOLORCHANGED,WM_THEMECHANGED,WM_SYSCOLORCHANGE), thatseveral other related settings needed the same treatment, and that a static-event-only design risks
the classic leak of rooting every subscriber for the application's lifetime. The proposal below is
the result: one immutable settings snapshot and one leak-free notification path.
SystemVisualSettings.TextScaleFactorrepresents Settings > Accessibility > Text size, notdisplay DPI scaling. DPI remains covered by
HighDpiMode,WM_DPICHANGED, and the existing DPIevents.
Note also: Animation can also be controlled via the Windows A11Y settings, and using
VisualStylesModecontrolled styles are including a bunch of different Animations:API proposal
Usage
Applications that replace fonts repeatedly should retain and dispose the font instances they own.
The framework deliberately does not perform automatic font scaling or relayout — font and layout
policies are application-specific.
Rationale and alternative designs
contrast, animations, keyboard cues, and focus-border metrics all arrive through the same set of
Windows messages; one type with a flags-based
Changedlets consumers early-out cheaply instead ofwiring up several independent events.
Controlinstance event/virtual instead of relying only on the staticApplicationevent: astatic-only design requires every subscribing control/form to unsubscribe or leak; the
Controlcascade (parent → children, modeled on the existing
OnSystemColorsChangedpattern) is scoped tothe control's own lifetime and needs no subscription management.
Changedinstead of separate events per category: keeps the notificationsurface to one event while still letting consumers react selectively.
GetWindowsAccentColor()method and aseparate
SystemTextSize/SystemTextSizeChangedpair — this would have required a second,near-identical mechanism per additional setting (high contrast, animations, etc.) and duplicated
the message-normalization logic.
Behavior
WM_SETTINGCHANGE,WM_DWMCOLORIZATIONCOLORCHANGED,WM_THEMECHANGED, andWM_SYSCOLORCHANGE;SystemVisualSettingsChangedis raised once per actualtransition, with
Changedreporting only the categories that differ from the previous snapshot.Control.OnSystemVisualSettingsChanged; existing applications do not change behavior.Will this feature affect UI controls?
The API does not automatically change existing controls. It provides the shared settings snapshot
and change notification that renderers, applications, and follow-up control APIs use to update their
own rendering, measurement, and layout.
Designer impact:
Control.SystemVisualSettingsChangedappears as a standard event. There are nonew serialization requirements.
Accessibility impact: Applications and controls can react when the user changes Windows
accent color, high contrast, text size, animation, or keyboard-cue settings while running.
Localization: No user-visible strings are introduced.
I'll attach an animated GIF demo later.