The central static theme registry for all Beep controls. Manages theme discovery, switching, DPI scaling, and form-style-to-theme mapping. All controls listen to its events to repaint automatically when the active theme or DPI changes.
BeepThemesManager is a static class — no instantiation required. At startup it:
Scans all loaded assemblies for types implementing IBeepTheme.
Loads the fixed list of 55+ predefined themes from TheTechIdea.Beep.Vis.Modules2.0.
Ensures a fallback DefaultBeepTheme always exists.
Exposes static events (ThemeChanged, FormStyleChanged, DpiChanged) that every control subscribes to.
Namespace:TheTechIdea.Beep.Winform.Controls.ThemeManagement Type: Static class — use directly, no new needed. Key entry points:BeepThemesManager.CurrentThemeName · BeepThemesManager.CurrentStyle · BeepThemesManager.ThemeChanged
Events
Event
Args Type
When Fired
ThemeChanged
ThemeChangeEventArgs
Every time CurrentThemeName is assigned a new value. All controls subscribe here to call ApplyTheme().
FormStyleChanged
StyleChangeEventArgs
Every time CurrentStyle changes. Automatically triggers ThemeChanged via the corresponding theme name.
DpiChanged
EventArgs
When DpiScaleFactor changes (monitor-move). Controls should update layout metrics and Invalidate() — not call ApplyTheme().
ThemeChangeEventArgs
Property / Method
Type
Description
OldThemeName
string
Name of the theme before the change.
NewThemeName
string
Name of the newly active theme.
OldTheme
IBeepTheme
The previous theme object.
NewTheme
IBeepTheme
The new theme object (ready to use for painting).
IsThemeChanged()
bool
True when old and new theme names differ.
IsAnyChange()
bool
True when any aspect of the event represents a change.
GetChangeSummary()
string
Human-readable summary of what changed.
ToString()
string
"Theme changed from X to Y"
StyleChangeEventArgs
Property
Type
Description
OldStyle
FormStyle
The FormStyle value before the change.
NewStyle
FormStyle
The new FormStyle value.
Static Properties
Theme Selection
Property
Type
Description
CurrentThemeName
string
Get or set the name of the active theme. Setting fires ThemeChanged.
CurrentTheme
IBeepTheme
Read-only. Returns the IBeepTheme object for the current theme name.
CurrentStyle
FormStyle
Get or set the current FormStyle. Setting resolves the corresponding theme and fires both FormStyleChanged and ThemeChanged.
DefaultTheme
IBeepTheme
The resolved fallback theme (always non-null after initialization).
DefaultThemeGuid
Guid
All-zeros GUID used as the default theme identifier sentinel.
Horizontal DPI scale factor. Read-only; updated via UpdateDpiScaleFactors.
DpiScaleY
float
1.0
Vertical DPI scale factor. Read-only.
EnableDpiFontScaling
bool
false
Enable manual DPI multiplication on fonts. Keep false.
WinForms already auto-scales Point fonts; enabling this causes double-scaling
and oversized text on high-DPI monitors.
Only enable for pure GDI custom painters that need physical pixel sizes.
Static Methods
Theme Lifecycle
Method
Returns
Description
InitializeThemes()
void
Clear and re-discover all themes from loaded assemblies. Called automatically by the static constructor; call again to reload after dynamically loading plugin assemblies.
ResetThemes()
void
Alias for InitializeThemes(). Useful in tests or plugin-reload scenarios.
AddTheme(theme)
void
Register a custom IBeepTheme. Assigns a GUID automatically if missing. Duplicate GUIDs are ignored.
RemoveTheme(theme)
void
Remove a theme by its GUID.
Theme Retrieval
Method
Returns
Description
GetTheme(name)
IBeepTheme
Return theme by name, or the default theme if not found.
GetDefaultTheme()
IBeepTheme
Priority: "DefaultTheme" → "DefaultBeepTheme" → any registered theme.
GetThemes()
List<IBeepTheme>
Return a snapshot copy of the full theme collection.
GetThemeNames()
List<string>
Return all registered theme names.
GetThemeName(theme)
string
Return the ThemeName of a theme object, or "DefaultBeepTheme" if null.
GetGuidFromTheme(theme)
string
Return the GUID string, or the zero-GUID placeholder if null.
ThemeExists(name)
bool
Returns true if a theme with the given name is registered.
Theme Switching
Method
Description
SetCurrentTheme(string name)
Switch by name if it exists; fires ThemeChanged.
SetCurrentTheme(IBeepTheme theme)
Switch by object reference; auto-registers if not already known; fires ThemeChanged.
SetCurrentStyle(FormStyle style)
Switch by FormStyle enum; resolves the paired theme; fires FormStyleChanged + ThemeChanged.
FormStyle to Theme Mapping
Method
Returns
Description
GetThemeForFormStyle(FormStyle)
IBeepTheme
Return the theme object paired with a given FormStyle.
GetThemeNameForFormStyle(FormStyle)
string
Return the theme name for a given FormStyle (see mapping table below).
Font Helpers
Method
Returns
Description
ToFont(TypographyStyle)
Font
Create a logical Point font. No DPI scaling applied — WinForms auto-scales these. For standard control text.
ToFont(TypographyStyle, bool)
Font
applyDpiScaling parameter is silently ignored for backward compatibility. Behaves identically to the one-argument overload.
ToFontForControl(TypographyStyle, Control)
Font
DPI-scaled font for custom GDI painters only. Uses the control's current DPI to calculate physical pixel size. Do not use for standard WinForms text properties.
ToFont(family, size, weight, style)
Font
Create a font from raw parameters. Falls back to Segoe UI automatically when the requested family is unavailable.
ScaleFontForDpi(Font)
Font
Multiply an existing font by the global DpiScaleFactor. No-op when EnableDpiFontScaling is false (default).
ScaleFontForControl(Font, Control)
Font
Multiply an existing font by a specific control's current DPI scale factor.
DPI Management
Method
Description
UpdateDpiFromControl(Control)
Detect and apply the DPI of a specific control. Call from OnDpiChanged in forms.
UpdateDpiFromSystem()
Apply the system-wide DPI. Call once at application startup before any form is created.
UpdateDpiScaleFactors(scaleX, scaleY)
Set both axes explicitly. Fires DpiChanged only when values change by more than 0.01.
Persistence
Method
Description
SaveTheme(theme, filePath)
XML-serialize a theme to the given file path.
LoadTheme(filePath)
XML-deserialize a theme from file. Registers it in the collection automatically.
Misc
Method
Description
ConfigureDefaultTheme(BeepTheme)
Apply sensible dark or light color defaults based on whether the name contains "Dark". Good starting point for new themes.
FormStyle → Theme Name Mapping
Setting CurrentStyle automatically resolves and activates the paired theme via this mapping:
FormStyle
Theme Name
FormStyle
Theme Name
Modern
ModernTheme
Nordic
NordicTheme
Minimal
MinimalTheme
iOS
iOSTheme
MacOS
MacOSTheme
Ubuntu
UbuntuTheme
Fluent
FluentTheme
KDE
KDETheme
Material
MaterialDesignTheme
ArcLinux
ArcLinuxTheme
Cartoon
CartoonTheme
Dracula
DraculaTheme
ChatBubble
ChatBubbleTheme
Solarized
SolarizedTheme
Glass
GlassTheme
OneDark
OneDarkTheme
Metro
MetroTheme
GruvBox
GruvBoxTheme
Metro2
Metro2Theme
Nord
NordTheme
GNOME
GNOMETheme
Tokyo
TokyoTheme
NeoMorphism
NeoMorphismTheme
Paper
PaperTheme
Glassmorphism
GlassmorphismTheme
Neon
NeonTheme
Brutalist
BrutalistTheme
Holographic
HolographicTheme
Retro
RetroTheme
Terminal
TerminalTheme
Cyberpunk
CyberpunkTheme
Custom
DefaultTheme
(any other)
DefaultTheme
Built-in Predefined Themes
All themes below are auto-discovered and registered at startup from TheTechIdea.Beep.Vis.Modules2.0. They are also dynamically loaded by AddPredefinedThemes() via reflection:
DefaultTheme
WinterTheme
CandyTheme
ZenTheme
RetroTheme
RoyalTheme
HighlightTheme
DarkTheme
OceanTheme
LightTheme
PastelTheme
MidnightTheme
SpringTheme
ForestTheme
NeonTheme
RusticTheme
GalaxyTheme
DesertTheme
VintageTheme
ModernDarkTheme
MaterialDesignTheme
NeumorphismTheme
GlassmorphismTheme
FlatDesignTheme
CyberpunkNeonTheme
GradientBurstTheme
HighContrastTheme
MonochromeTheme
LuxuryGoldTheme
SunsetTheme
AutumnTheme
EarthyTheme
ArcLinuxTheme
BrutalistTheme
CartoonTheme
ChatBubbleTheme
CyberpunkTheme
DraculaTheme
FluentTheme
GlassTheme
GNOMETheme
GruvBoxTheme
HolographicTheme
iOSTheme
KDETheme
MacOSTheme
Metro2Theme
MetroTheme
MinimalTheme
NeoMorphismTheme
NordicTheme
NordTheme
OneDarkTheme
PaperTheme
SolarizedTheme
TerminalTheme
TokyoTheme
UbuntuTheme
Creating a Custom Theme
Implement IBeepTheme (or inherit from BeepTheme) and register it once at startup:
// Register once at application startup (before Application.Run)
BeepThemesManager.AddTheme(new MyBrandTheme());
// Activate it - all subscribed controls repaint automatically
BeepThemesManager.SetCurrentTheme("MyBrandTheme");
Subscribing to Theme Events in Custom Controls
public class MyCustomControl : BeepControl
{
public MyCustomControl()
{
BeepThemesManager.ThemeChanged += OnThemeChanged;
BeepThemesManager.DpiChanged += OnDpiChanged;
BeepThemesManager.FormStyleChanged += OnFormStyleChanged;
}
private void OnThemeChanged(object sender, ThemeChangeEventArgs e)
{
// e.NewTheme is the ready-to-use IBeepTheme
ApplyTheme(e.NewTheme);
Invalidate();
}
private void OnDpiChanged(object sender, EventArgs e)
{
// DPI changed - update layout metrics but do NOT call ApplyTheme()
UpdateLayout();
Invalidate();
}
private void OnFormStyleChanged(object sender, StyleChangeEventArgs e)
{
// Optional: adjust chrome for the new style
UpdateBorderForStyle(e.NewStyle);
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
// CRITICAL: always unsubscribe - static events hold strong references
BeepThemesManager.ThemeChanged -= OnThemeChanged;
BeepThemesManager.DpiChanged -= OnDpiChanged;
BeepThemesManager.FormStyleChanged -= OnFormStyleChanged;
}
base.Dispose(disposing);
}
}
Memory leak warning: Static events hold strong references to all subscribers. Always unsubscribe in Dispose(), otherwise controls are kept alive until the application exits.
DPI Handling
Beep uses WinForms automatic Point-font DPI scaling for all standard text. Custom GDI painters that need absolute pixel measurements must call ToFontForControl instead of ToFont.
Application startup
// Program.cs - before Application.Run()
Application.SetHighDpiMode(HighDpiMode.PerMonitorV2);
BeepThemesManager.UpdateDpiFromSystem();
protected override void OnPaint(PaintEventArgs e)
{
// Use ToFontForControl for physical pixel accuracy in custom GDI drawing
using var bodyFont = BeepThemesManager.ToFontForControl(
BeepThemesManager.CurrentTheme.Body1Typography, this);
e.Graphics.DrawString("Hello", bodyFont, Brushes.Black, ClientRectangle);
}
Switching Themes at Runtime
// By name
BeepThemesManager.SetCurrentTheme("DraculaTheme");
// By FormStyle - sets both form chrome and theme palette in one call
BeepThemesManager.SetCurrentStyle(FormStyle.Dracula);
// By object reference
IBeepTheme night = BeepThemesManager.GetTheme("MidnightTheme");
BeepThemesManager.SetCurrentTheme(night);
// Populate a ComboBox with all available themes
var names = BeepThemesManager.GetThemeNames();
cmbTheme.DataSource = names;
cmbTheme.SelectedIndexChanged += (s, e) =>
BeepThemesManager.SetCurrentTheme((string)cmbTheme.SelectedItem);
Theme Persistence
// Save a custom theme to an XML file
IBeepTheme custom = BeepThemesManager.GetTheme("MyBrandTheme");
BeepThemesManager.SaveTheme(custom, @"themes\mybrand.xml");
// Load it back (also registers it automatically)
IBeepTheme loaded = BeepThemesManager.LoadTheme(@"themes\mybrand.xml");
BeepThemesManager.SetCurrentTheme(loaded);
Best Practices
Never call ApplyTheme() inside the DpiChanged handler.
DPI change and theme change are intentionally separate events. Calling ApplyTheme()
on DPI change causes unnecessary font re-creation, multiple repaints, and potential double-scaling.
Always unsubscribe in Dispose().
Static event subscriptions prevent controls from being garbage-collected.
Use ToFont() for normal WinForms text, ToFontForControl() for custom GDI painters.ToFont() returns a logical Point font that WinForms auto-scales.
ToFontForControl() returns a physical pixel font for raw GDI measurements.
Leave EnableDpiFontScaling = false (the default).
Enabling it on a WinForms app causes double-scaling (WinForms + manual) resulting in oversized text on high-DPI monitors.
Call UpdateDpiFromSystem() once at startup.
For forms that can move between monitors also call UpdateDpiFromControl(this)
inside OnDpiChanged.
Register custom themes before calling Application.Run().
Controls are created first and may miss a ThemeChanged event if the theme is added after the window is shown.
Use ConfigureDefaultTheme() as a quick baseline.
It sets sensible dark/light colors and padding so you only override the properties your brand needs.
Prefer SetCurrentStyle(FormStyle) over SetCurrentTheme(name)
when you want the form chrome and control palette to stay visually consistent — both change together in one call.