Styling System
The complete painting architecture for Beep controls. A three-layer system: BeepThemesManager (color palette) → BeepStyling (rendering coordinator) → 170+ specialized painters. BaseControl and BeepiFormPro each own a slice of this pipeline.
System Architecture
Three independent layers cooperate to paint everything on screen:
| Layer | Class / Namespace | Responsibility |
|---|---|---|
| 1 — Theme Layer | BeepThemesManagerThemeManagement/ |
Holds the active IBeepTheme (color palette, typography, spacing tokens).
Fires ThemeChanged / DpiChanged events.
All controls subscribe and call ApplyTheme() in response.
|
| 2 — Style Coordinator | BeepStylingStyling/BeepStyling.cs |
Central static class. Maps FormStyle ↔ BeepControlStyle,
routes every paint call to the correct specialist painter,
and exposes helper shortcuts (GetRadius, GetBorderColor, …).
Does not draw anything itself.
|
| 3 — Painter Subsystems | 7 painter folders 170+ classes |
Each painter class owns one operation (background, border, text, shadow, …) for one design system. Painters call the 5 helper systems for style-specific values (colors, spacing, borders, shadows, typography). |
How BaseControl fits in
BaseControl is the root for every Beep UI control. Its painting pipeline:
OnPaint→DrawContent(g)EnsurePainter()— selects anIBaseControlPainterbased onPainterKind- Painter calls
BeepStyling.PaintStyleBackground / PaintStyleBorder / PaintStyleTextusing the control's currentBeepControlStyle - Derived control overrides
DrawContentto add specific content insideDrawingRect
How BeepiFormPro fits in
BeepiFormPro is the themed borderless form host. Its pipeline is parallel but separate from the control pipeline:
FormStyleproperty changes →ApplyFormStyle()BeepStyling.GetControlStyle(FormStyle)→ syncsBeepStyling.CurrentControlStyleso controls inside the form automatically pick up the same stylePaintersFactory.GetPainter(FormStyle)→ returns a cachedIFormPainterIFormPainter.CalculateLayoutAndHitAreas()positions caption bar, buttons, title;IFormPainter.PaintBackground / PaintCaptionBar / PaintBorderrender each layer
BeepiFormPro.FormStyle calls BeepStyling.ApplyFormStyle() which sets BeepStyling.CurrentControlStyle. Every control inside that form reads BeepStyling.CurrentControlStyle during paint, so the form chrome and all controls always share the same design system.
BeepStyling — The Coordinator
Static class in TheTechIdea.Beep.Winform.Controls.Styling. Single source of truth for which design system is currently active.
Global State
| Member | Type | Description |
|---|---|---|
CurrentControlStyle | BeepControlStyle | The active design system for all controls. Default: Minimal. |
CurrentTheme | IBeepTheme | The active theme object (colors, typography). Set from BeepThemesManager.CurrentTheme. |
UseThemeColors | bool | When true (default), color methods prefer theme colors over hard-coded style defaults. |
ImageCachedPainters | Dictionary<string, ImagePainter> | Image painter cache. Shared across all controls. |
Style Management Methods
| Method | Description |
|---|---|
SetControlStyle(BeepControlStyle) | Change the global style and fire ControlStyleChanged event. |
GetControlStyle() | Return CurrentControlStyle. |
ApplyFormStyle(FormStyle) | Map a FormStyle to the corresponding BeepControlStyle and set it. |
GetControlStyle(FormStyle) | Return the BeepControlStyle for a given FormStyle (pure lookup, no side effects). |
GetFormStyle(BeepControlStyle) | Reverse lookup: BeepControlStyle → FormStyle. |
GetThemeStyle(BeepControlStyle) | Return the theme name associated with a control style. |
Paint Entry Points
| Method | Description |
|---|---|
PaintStyleBackground(g, path) | Paint background using CurrentControlStyle. |
PaintStyleBackground(g, path, BeepControlStyle) | Paint background for a specific style. |
PaintStyleBorder(g, path, isFocused) | Paint border using CurrentControlStyle and focus state. |
PaintStyleBorder(g, path, isFocused, BeepControlStyle) | Paint border for a specific style. |
PaintStyleText(g, path, text, isFocused) | Paint text using CurrentControlStyle. |
PaintStyleText(g, path, text, isFocused, BeepControlStyle) | Paint text for a specific style. |
PaintStyleSpinnerButtons(g, upPath, downPath, isFocused, style) | Paint numeric up/down buttons for a specific style. |
Style Value Shortcuts
| Method | Returns | Description |
|---|---|---|
GetRadius(BeepControlStyle) | int | Corner radius in pixels for the style. |
GetBorderThickness(BeepControlStyle) | float | Border stroke width. |
GetPadding(BeepControlStyle) | int | Internal padding. |
GetBackgroundColor(BeepControlStyle) | Color | Background color (theme-aware). |
GetForegroundColor(BeepControlStyle) | Color | Foreground/text color (theme-aware). |
GetBorderColor(BeepControlStyle) | Color | Border color (theme-aware). |
GetThemeColor(themeColorKey) | Color | Read a named color from the current theme (e.g., "Primary", "Accent"). |
CreateControlPath(bounds, FormStyle) | GraphicsPath | Create the clipping/rendering path for a control using a form style. |
CreateControlStylePath(bounds, BeepControlStyle) | GraphicsPath | Create the path using a direct control style. |
Painter Subsystems (7 Folders)
Each folder contains style-specific painter classes. All painters are static and stateless; they receive all context via parameters.
| Folder | Classes | Signature Pattern | Responsibility |
|---|---|---|---|
BackgroundPainters/ |
36+ | Paint(Graphics, GraphicsPath, BeepControlStyle, IBeepTheme, bool) |
Fill the control background. Effects include: flat fill, gradient, glass/acrylic, neon glow, neumorphism, mica, frosted glass, dark glow, gradient burst, and more. |
BorderPainters/ |
27 | Paint(Graphics, GraphicsPath, bool, BeepControlStyle, IBeepTheme, bool, ControlState) |
Draw border strokes. Variations: solid, double, dashed, glowing, focus-ring, accent-side, thick/thin. |
TextPainters/ |
5 | Paint(Graphics, Rectangle, string, bool, BeepControlStyle, IBeepTheme, bool) |
Render text with the correct font, color, weight, and spacing for each design system family. |
SpinnerButtonPainters/ |
26 | PaintButtons(Graphics, Rectangle, Rectangle, bool, BeepControlStyle, IBeepTheme, bool) |
Render up/down arrow buttons for numeric controls with style-appropriate visuals. |
ShadowPainters/ |
27 | Paint(Graphics, GraphicsPath, int, IBeepTheme) |
Render drop shadows and elevation effects below controls. Includes dual shadows for neumorphism. |
PathPainters/ |
23 | Fill(Graphics, GraphicsPath, BeepControlStyle, IBeepTheme) |
Fill arbitrary GraphicsPath shapes (shapes, overlays, decorations) with style-appropriate brushes. |
ImagePainters/ |
1 (static) | See StyledImagePainter | Cached image rendering with rounded corners, shape clipping, tint, and opacity. Supports 10+ geometric clip shapes. |
Factory Classes
Each painter folder has a companion factory for lookup and caching:
| Factory | Location | Method |
|---|---|---|
BackgroundPainterFactory | BackgroundPainters/ | CreatePainter(BeepControlStyle) → IBackgroundPainter |
BorderPainterFactory | BorderPainters/ | CreatePainter(BeepControlStyle) → IBorderPainter |
ShadowPainterFactory | Shadows/ | CreatePainter(BeepControlStyle) → IShadowPainter |
PaintersFactory (Forms) | Forms/ModernForm/ | GetPainter(FormStyle) → IFormPainter (cached) |
Style Helper Systems (5 Systems)
All helpers are static classes that accept BeepControlStyle and return design-system-specific values via switch expressions. No base classes — every value is explicit.
| Class | Location | Key Methods |
|---|---|---|
StyleColors |
Colors/ | GetBackground(style), GetForeground(style), GetBorder(style), GetAccent(style), GetPrimary(style) |
StyleSpacing |
Spacing/ | GetPadding(style), GetMargin(style), GetItemHeight(style) |
StyleBorders |
Borders/ | GetRadius(style), GetBorderWidth(style), HasBorder(style) |
StyleShadows |
Shadows/ | HasShadow(style), GetShadowOpacity(style), GetShadowBlur(style), UsesDualShadows(style) |
StyleTypography |
Typography/ | GetFontFamily(style), GetFontSize(style), GetFontWeight(style), GetLetterSpacing(style) |
BaseControl Painting Pipeline
Every Beep control inherits from BaseControl (ContainerControl). Understanding this pipeline is essential for creating custom controls.
File Structure
| File | Responsibility |
|---|---|
BaseControl.cs | Core class declaration, private fields, IsReadyForDrawing guard, state flags (IsHovered, IsPressed, IsFocused) |
BaseControl.Methods.cs | EnsurePainter(), InvokeBorderPainter(), ApplyTheme(), UpdateBorderPainter() |
BaseControl.Properties.cs | All public properties: PainterKind, BorderPainterStyle, Theme, IsChild, layout properties |
BaseControl.LayoutManagement.cs | DrawingRect calculation, label/icon/error layout |
BaseControl.Events.cs | Mouse events, keyboard events, theme subscription/unsubscription |
BaseControl.LabelandError.cs | Label text, helper text, error message regions |
BaseControl.Tooltip.cs | Tooltip creation and theme theming |
BaseControl.Win32.cs | WM_NCHITTEST overrides, flicker reduction |
Helpers/Painters/IBaseControlPainter.cs | Painter interface: UpdateLayout(), Paint(), UpdateHitAreas() |
Helpers/Painters/ClassicBaseControlPainter.cs | Default painter: shadow → fill → border → gradient → image → text |
Render Sequence
protected override void OnPaint(PaintEventArgs e)
{
if (!IsReadyForDrawing) return; // 1. Guard
if (AllowBaseControlClear) e.Graphics.Clear(BackColor);
DrawContent(e.Graphics); // 2. Entry
}
protected virtual void DrawContent(Graphics g)
{
EnsurePainter(); // 3. Select painter
_painter?.UpdateLayout(this); // 4. Compute rects
_painter?.Paint(g, this); // 5. Render outer chrome
_painter?.UpdateHitAreas(this); // 6. Register hit zones
// DrawingRect is now ready — derived control paints content here
}
Painter Kinds
| PainterKind | Painter Class | Visual Style |
|---|---|---|
Classic (default) | ClassicBaseControlPainter | Traditional WinForms; configurable borders, shadows, gradients, state colors |
None | — (no painter) | Control handles ALL rendering itself inside DrawContent |
Auto | ClassicBaseControlPainter | Alias for Classic |
Border Painter Style (independent)
Separate from PainterKind. The BorderPainterStyle property selects a BeepControlStyle-specific border painter that is applied after the main painter renders the background:
// In a custom control constructor
PainterKind = BaseControlPainterKind.Classic; // outer chrome
BorderPainterStyle = BeepControlStyle.Fluent2; // border look
UpdateBorderPainter() calls BorderPainterFactory.CreatePainter(style) and also syncs BorderRadius, BorderThickness, and BorderColor from the style defaults.
Theme Subscription Pattern
// BaseControl automatically subscribes in its constructor:
BeepThemesManager.ThemeChanged += (s, e) => {
_currentTheme = e.NewTheme;
ApplyTheme();
Invalidate();
};
// ApplyTheme() copies theme tokens to control properties:
public virtual void ApplyTheme()
{
ForeColor = _currentTheme.ForeColor;
BorderColor = _currentTheme.BorderColor;
BackColor = IsChild ? Parent.BackColor : _currentTheme.BackColor;
_textFont = BeepThemesManager.ToFont(_currentTheme.BodyMedium);
// ...
}
Creating a Custom Control
public class MyBeepLabel : BaseControl
{
public MyBeepLabel()
{
PainterKind = BaseControlPainterKind.Classic;
BorderPainterStyle = BeepControlStyle.Minimal;
IsChild = true; // transparent background
}
protected override void DrawContent(Graphics g)
{
base.DrawContent(g); // paints chrome into DrawingRect
// DrawingRect is the inner content area after padding/border
using var sf = new StringFormat { Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center };
g.DrawString(Text, _textFont, new SolidBrush(ForeColor), DrawingRect, sf);
}
}
BeepiFormPro Painting Pipeline
BeepiFormPro is a borderless Form subclass. Unlike controls, it has its own separate painter contract (IFormPainter) and factory.
File Structure
| File | Responsibility |
|---|---|
BeepiFormPro.cs | Main form class, FormStyle property, ApplyFormStyle() |
BeepiFormPro.Core.cs | EnsureLayoutCalculated(), caching (_layoutDirty, _lastLayoutSize), InvalidateLayout() |
BeepiFormPro.Drawing.cs | OnPaint → calls ActivePainter.PaintBackground / PaintCaptionBar / PaintBorder |
BeepiFormPro.Theme.cs | ApplyTheme(), BeepThemesManager.ThemeChanged subscription |
BeepiFormPro.Events.cs | Mouse events for caption drag, resize, button clicks |
BeepiFormPro.Win32.cs | WM_NCHITTEST, borderless window hit testing |
Managers/BeepiFormProHitAreaManager.cs | Registers named hit zones (close, minimize, maximize, title, resize grips) |
Managers/BeepiFormProInteractionManager.cs | Translates mouse events to hit zone actions |
PaintersFactory.cs | Caches one IFormPainter per FormStyle |
Painters/IFormPainter.cs | Contract: CalculateLayoutAndHitAreas, PaintBackground, PaintCaptionBar, PaintBorder |
IFormPainter Contract
public interface IFormPainter
{
void CalculateLayoutAndHitAreas(BeepiFormPro owner);
void PaintBackground(Graphics g, BeepiFormPro owner);
void PaintCaptionBar(Graphics g, BeepiFormPro owner);
void PaintBorder(Graphics g, BeepiFormPro owner);
}
Each FormStyle has exactly one painter class (e.g., DraculaFormPainter, MacOSFormPainter). The painter owns everything: corner radius, caption height, window button positions, and visual effects for that style.
Layout Caching
To avoid recalculating hit areas on every paint, BeepiFormPro.Core.cs tracks a dirty flag. Layout only recalculates when:
- Form size changes
FormStylechanges- Caption/button visibility changes
InvalidateLayout()is called explicitly
Available FormStyle Painters (40+)
- ArcLinuxFormPainter
- BrutalistFormPainter
- CartoonFormPainter
- ChatBubbleFormPainter
- CyberpunkFormPainter
- DraculaFormPainter
- FluentFormPainter
- GlassFormPainter
- GlassmorphismFormPainter
- GNOMEFormPainter
- GruvBoxFormPainter
- HolographicFormPainter
- iOSFormPainter
- KDEFormPainter
- LinearFormPainter
- MacOSFormPainter
- MaterialFormPainter
- MaterialYouFormPainter
- Metro2FormPainter
- MetroFormPainter
- MinimalFormPainter
- ModernFormPainter
- NeoMorphismFormPainter
- NeonFormPainter
- NextJSFormPainter
- NordFormPainter
- NordicFormPainter
- OneDarkFormPainter
- PaperFormPainter
- RadixUIFormPainter
- RetroFormPainter
- ShadcnFormPainter
- SolarizedFormPainter
- TerminalFormPainter
- TokyoFormPainter
- UbuntuFormPainter
StyledImagePainter
Static class in Styling/ImagePainters/. Handles all image rendering across the system with built-in caching, shape clipping, tinting, and opacity.
Caching
| Cache | Key | Purpose |
|---|---|---|
_imageCache | imagePath | Decoded Image objects from disk/resource |
_painterCache | imagePath | ImagePainter objects for path-based drawing |
_tintedCache | path+tint+opacity | Pre-rendered tinted bitmaps |
All caches are ConcurrentDictionary — thread-safe for background pre-rendering.
Core Paint Methods
| Method | Description |
|---|---|
Paint(g, GraphicsPath, imagePath, BeepControlStyle) | Paint image clipped to a path with style-appropriate corner radius. |
Paint(g, GraphicsPath, imagePath) | Paint image clipped to a path with no radius (raw path clip). |
PaintWithTint(g, path, imagePath, tint, opacity, cornerRadius) | Paint with color tint overlay and opacity. Result is cached. |
Shape Clip Methods
| Method | Shape |
|---|---|
PaintInCircle(g, cx, cy, radius, imagePath, tint?, opacity) | Circle |
PaintInTriangle(g, cx, cy, size, imagePath, rotation, tint?, opacity) | Triangle |
PaintInStar(g, cx, cy, outer, inner, imagePath, points, rotation, tint?, opacity) | N-pointed star |
PaintInHexagon(g, cx, cy, size, imagePath, rotation, tint?, opacity) | Hexagon |
PaintInPentagon(g, cx, cy, size, imagePath, rotation, tint?, opacity) | Pentagon |
PaintInOctagon(g, cx, cy, size, imagePath, rotation, tint?, opacity) | Octagon |
PaintInPolygon(g, cx, cy, size, sides, imagePath, rotation, tint?, opacity) | Any regular N-gon |
Usage Example
protected override void DrawContent(Graphics g)
{
base.DrawContent(g);
// Paint avatar image clipped to a circle
float cx = DrawingRect.X + DrawingRect.Width / 2f;
float cy = DrawingRect.Y + DrawingRect.Height / 2f;
float r = Math.Min(DrawingRect.Width, DrawingRect.Height) / 2f - 2f;
StyledImagePainter.PaintInCircle(g, cx, cy, r, ImagePath);
// Or paint with a blue tint at 80% opacity:
StyledImagePainter.PaintWithTint(g, _clipPath, ImagePath,
Color.FromArgb(100, 0, 120, 215), opacity: 0.8f, cornerRadius: 8);
}
FormStyle ↔ BeepControlStyle Mapping
When BeepiFormPro.FormStyle changes, BeepStyling.ApplyFormStyle() calls GetControlStyle(formStyle) to sync all controls inside the form to the same visual system:
| FormStyle | BeepControlStyle | FormStyle | BeepControlStyle |
|---|---|---|---|
| Modern | Modern | Nordic | Nordic |
| Minimal | Minimal | iOS | iOS15 |
| MacOS | MacOSBigSur | Ubuntu | Ubuntu |
| Fluent | Fluent | KDE | Kde |
| Material | Material | ArcLinux | ArcLinux |
| Cartoon | Cartoon | Dracula | Dracula |
| ChatBubble | ChatBubble | Solarized | Solarized |
| Glass | GlassAcrylic | OneDark | OneDark |
| Metro | Metro | GruvBox | GruvBox |
| Metro2 | Metro2 | Nord | Nord |
| GNOME | Gnome | Tokyo | Tokyo |
| NeoMorphism | Neumorphism | Paper | Paper |
| Glassmorphism | Glassmorphism | Neon | Neon |
| Brutalist | Brutalist | Holographic | Holographic |
| Retro | Retro | Terminal | Terminal |
| Cyberpunk | Cyberpunk | Shadcn | Shadcn |
| MaterialYou | MaterialYou | RadixUI | RadixUI |
| NextJS | NextJS | Linear | Linear |
| Custom | None | (default) | Minimal |
FormStyle and default to FormStyle.Custom in the reverse lookup. They are available for individual controls but cannot be set on the form chrome directly.
BeepControlStyle Enum
60+ styles grouped by origin:
Web Frameworks & Design Systems
Material3, AntDesign, Fluent2, MaterialYou, Windows11Mica, MacOSBigSur, ChakraUI, TailwindCard, NotionMinimal, VercelClean, StripeDashboard, Bootstrap, FigmaCard, PillRail, Apple, Shadcn, RadixUI, NextJS, Linear
Dark / Atmospheric
DarkGlow, DiscordStyle, GradientModern, GlassAcrylic, Neumorphism, Glassmorphism, NeoMorphism, Neon, NeonGlow, DarkGlow, Holographic, Cyberpunk
Desktop OS
Minimal, Modern, Fluent, Material, Metro, Metro2, MacOS, iOS15, Ubuntu, Gnome, Kde, Cinnamon, Elementary, ArcLinux, Nordic, Nord
Editor / Terminal Themes
Dracula, Solarized, OneDark, GruvBox, Tokyo, Terminal, Paper
Visual Aesthetics
Cartoon, ChatBubble, Retro, Brutalist, NeoBrutalist, Gaming, HighContrast, Office, FinSet, WebFramework, Effect
Best Practices
-
Never copy-paste style values into a control.
Always ask
StyleColors,StyleBorders, etc. so the control automatically stays correct when the design system evolves. -
Let
BeepStylingroute painter calls; don't call individual painters directly from controls. The routing switch inBeepStyling.PaintStyleBackground()is the single maintenance point for adding new styles. -
Always set
BeepStyling.CurrentThemewheneverBeepThemesManager.CurrentThemechanges.BeepiFormPro.ApplyTheme()does this automatically; your custom root forms must do the same. -
Use
PainterKind = BaseControlPainterKind.Noneonly when the control is doing fully custom rendering. Anything set toNonebypasses the shadow, border, and background system entirely. - Keep painters stateless. All context (style, theme, focus state) must come in as method parameters. Painters must not cache mutable state between calls.
-
Use
StyledImagePainterfor every image draw operation. It handles icon loading, SVG fallback paths, DPI-aware sizing, and the shared cache. Do not callg.DrawImage()with an unconditionalImage.FromFile()insideOnPaint. -
Call
InvalidateLayout()after property changes that affect size. ForBeepiFormPro, this clears the cached border path and hit areas so the painter recalculates on the next paint cycle. -
Implement
IFormPainterfor custom form skins. Register it inPaintersFactory.CreatePainter()under a newFormStylevalue. That is the only change required system-wide.