Dynamic Theme & FormStyle Switching

Build a runtime theme switcher using BeepThemesManager and BeepiFormPro.FormStyle:

Runtime theme and FormStyle switcher

using TheTechIdea.Beep.Winform.Controls;
using TheTechIdea.Beep.Winform.Controls.Forms.ModernForm;
using TheTechIdea.Beep.Winform.Controls.ThemeManagement;

public class SettingsForm : BeepiFormPro
{
    public SettingsForm()
    {
        Text = "Settings";
        Size = new Size(500, 400);
        FormStyle = FormStyle.Fluent;

        // Theme selector
        var themeCombo = new BeepComboBox();
        themeCombo.Size = new Size(220, 35);
        themeCombo.Location = new Point(20, 70);
        
        // Populated from auto-discovered themes
        foreach (var theme in BeepThemesManager.Themes)
            themeCombo.Items.Add(theme.ThemeName);
        
        themeCombo.SelectedIndexChanged += (s, e) =>
        {
            var name = themeCombo.SelectedItem?.ToString();
            if (!string.IsNullOrEmpty(name))
                BeepThemesManager.ApplyTheme(name);
        };

        // FormStyle selector
        var styleCombo = new BeepComboBox();
        styleCombo.Size = new Size(220, 35);
        styleCombo.Location = new Point(20, 120);
        
        foreach (FormStyle style in Enum.GetValues(typeof(FormStyle)))
            styleCombo.Items.Add(style.ToString());
        
        styleCombo.SelectedIndexChanged += (s, e) =>
        {
            if (Enum.TryParse<FormStyle>(
                styleCombo.SelectedItem?.ToString(), out var fs))
            {
                FormStyle = fs; // BeepiFormPro switches painter instantly
            }
        };

        Controls.Add(themeCombo);
        Controls.Add(styleCombo);
    }
}

Working with BaseControl Painters

The PainterKind property switches the entire rendering strategy at runtime. Under the hood, BaseControl calls EnsurePainter() which creates the appropriate painter from the PaintersFactory:

Switching PainterKind at runtime

// Render pipeline: OnPaint → EnsurePainter → _painter.Paint → DrawingRect
// Derived controls paint their content into DrawingRect only

// Switch painter at runtime
myPanel.PainterKind = BaseControlPainterKind.Auto;          // Follow global style
myPanel.PainterKind = BaseControlPainterKind.Glassmorphism;  // Frosted glass
myPanel.PainterKind = BaseControlPainterKind.Neumorphism;    // Soft 3D
myPanel.PainterKind = BaseControlPainterKind.Card;           // Card with shadow
myPanel.PainterKind = BaseControlPainterKind.NeoBrutalist;   // Bold outlines

// Change the border/background style via BeepControlStyle (mapped from BeepStyling)
myButton.BorderPainter = BeepControlStyle.Material3;
myButton.BorderPainter = BeepControlStyle.FluentDesign;

Using BeepStyling Design Systems

BeepStyling manages 27 design systems. Setting CurrentControlStyle globally changes which background, border, shadow, and text painters are used:

Global design system switching

using TheTechIdea.Beep.Winform.Controls.Styling;

// Switch the entire app to a different design system
BeepStyling.SetControlStyle(BeepControlStyle.Material3);
// or BeepControlStyle.FluentDesign, iOS15, AntDesign, ChakraUI,
// TailwindCSS, Bootstrap5, Carbon, Atlassian, Lightning, etc.

// Each design system has its own painters in these folders:
// BackgroundPainters/ (36) — e.g., Material3BackgroundPainter
// BorderPainters/     (27) — e.g., FluentDesignBorderPainter
// ShadowPainters/     (27) — e.g., iOS15ShadowPainter
// TextPainters/       (5)  — e.g., MaterialTextPainter
// PathPainters/       (23) — shape/path rendering

// Query colors from the active style
Color primary = BeepStyling.GetColor("Primary");
Color bg = BeepStyling.GetColor("Background");

BeepiFormPro Form Painter Architecture

Each FormStyle maps to a dedicated IFormPainter class in the Painters/ subfolder. Key classes involved:

  • FormPainterMetrics — Calculates caption bar height, button sizes, padding, border widths
  • BeepiFormProHitAreaManager — Manages close/min/max button hit areas, title bar drag region
  • BeepiFormProInteractionManager — Handles hover, press, and release states for caption buttons
  • ActivePainter — The current IFormPainter instance (e.g., MaterialFormPainter, CyberpunkFormPainter)

FormStyle to painter mapping

// BeepiFormPro automatically selects the right painter:
// FormStyle.Material   → MaterialFormPainter
// FormStyle.MacOS      → MacOSFormPainter
// FormStyle.Fluent     → FluentFormPainter
// FormStyle.Cyberpunk  → CyberpunkFormPainter
// FormStyle.Terminal   → TerminalFormPainter
// ... and 30+ more

// When you change FormStyle, the form:
// 1. Creates/caches the new painter via EnsureLayoutCalculated()
// 2. Recalculates FormPainterMetrics
// 3. Updates HitAreaManager regions
// 4. Invalidates — triggers full repaint with new painter

Creating a Custom Control

To create your own control, inherit from BaseControl and override the paint method. The painter handles borders, backgrounds, and shadows — you only render your content into DrawingRect:

Custom control inheriting BaseControl

using TheTechIdea.Beep.Winform.Controls.Base;

public class MyCustomControl : BaseControl
{
    public string CustomText { get; set; } = "Hello";

    protected override void DrawContent(Graphics g, Rectangle drawRect)
    {
        // drawRect is already inset by the painter's borders/padding
        using var brush = new SolidBrush(CurrentTheme?.TextColor ?? Color.Black);
        using var font = new Font("Segoe UI", 12f);
        g.DrawString(CustomText, font, brush, drawRect.Location);
    }
}

// Usage
var custom = new MyCustomControl();
custom.PainterKind = BaseControlPainterKind.Card;
custom.CustomText = "My Content";
custom.Size = new Size(200, 100);

Theme Propagation in BeepiFormPro

BeepiFormPro.ApplyTheme() walks all child controls and applies the theme to any IBeepUIComponent:

Theme propagation in BeepiFormPro

// This happens automatically when BeepThemesManager fires ThemeChanged.
// You can also trigger it manually:
myForm.ApplyTheme(BeepThemesManager.GetTheme("Dracula"));

// Under the hood:
// 1. Sets CurrentTheme on the form
// 2. Iterates all Controls recursively
// 3. For each IBeepUIComponent child, calls child.ApplyTheme(theme)
// 4. Each child re-reads colors/fonts from the theme and invalidates