Overview

Namespace: TheTechIdea.Beep.Winform.Controls

Assembly: TheTechIdea.Beep.Winform.Controls

Inheritance: BeepSwitch → BaseControl → System.Windows.Forms.UserControl

Implements: IBeepComponent (via IBeepUIComponent methods)

BeepSwitch is a capsule-shaped toggle switch control with a pluggable painter system for rendering styles (iOS, Material3, Fluent2, Minimal). It supports smooth spring and cubic easing animations, drag-to-toggle thumb interaction, keyboard control (Space/Enter/Arrow keys), both horizontal and vertical orientations, icon library integration, custom image support, and full data-binding through IBeepComponent with boolean, string, and int coercion.

The control is implemented as a partial class split across nine files, with the core fields residing in BeepSwitch.Core.cs and the painter system delegating all visual rendering through ISwitchPainter implementations selected by BeepControlStyle.

Key Features

  • Pluggable Painter System: ISwitchPainter (iOSSwitchPainter, Material3SwitchPainter, Fluent2SwitchPainter, MinimalSwitchPainter) via SwitchPainterFactory.CreatePainter(ControlStyle).
  • Spring & Cubic Animations: Spring easing for Apple-style control styles (iOS15, MacOSBigSur, Apple) and cubic ease-out for others. 16ms timer at painter-specified frame rate.
  • Drag-to-Toggle: Drag the thumb along the track; progress is computed from mouse delta / total track distance. Snaps to nearest state on release.
  • Dual Orientation: Horizontal (L→R thumb travel) and Vertical (T→B) via SwitchOrientation enum. Track path generation adapts automatically.
  • Custom Labels: Separate OnLabel / OffLabel strings drawn at opposite ends by the active painter.
  • Icon Library: OnIconName / OffIconName resolve from IconsManagement.SvgsUI. Convenience presets: UseCheckmarkIcons(), UsePowerIcons(), UseLightIcons().
  • Legacy Images: OnImagePath / OffImagePath via BeepImage for track-background images with FileNameEditor support.
  • Keyboard: Space/Enter toggle, Left/Right set false/true, Up/Down set true/false. IsInputKey override enables focus and processing.
  • Focus Ring: Drawn around the track when Focused && ShowFocusCues. Colour from SwitchThemeHelpers.GetFocusRingColor.
  • Hit Areas: Track, thumb, OnLabel, and OffLabel click regions registered via BaseControl hit-area system for structural navigation.
  • Data Binding: SetValue (bool/string/int), GetValue, ClearValue, HasFilterValue, ToFilter, RefreshBinding, Oldvalue tracking.

Properties (13 total)

PropertyTypeDefaultCategoryDescription
CheckedboolfalseBehaviorSwitch state: On (true) or Off (false). Triggers animation + CheckedChanged event.
OrientationSwitchOrientationHorizontalAppearanceLayout direction: Horizontal or Vertical. Invalidates on change.
OnLabelstring"On"AppearanceLabel text displayed on the ON side of the track. Invalidates on change.
OffLabelstring"Off"AppearanceLabel text displayed on the OFF side of the track. Invalidates on change.
OnImagePathstring?nullAppearanceFile path (SVG/PNG/JPG) for ON-state track image. FileNameEditor at design time.
OffImagePathstring?nullAppearanceFile path for OFF-state track image. FileNameEditor at design time.
OnIconNamestring""AppearanceIcon library name for ON state (e.g. "check", "power", "lightbulb"). Resolved via IconsManagement.SvgsUI.
OffIconNamestring""AppearanceIcon library name for OFF state (e.g. "close", "power_off", "lightbulb_outline").
DragToToggleEnabledbooltrueBehaviorEnable drag-to-toggle. When true, thumb can be dragged along the track to change state.
BoundPropertystring"Checked"DataProperty name for data-binding resolution (IBeepComponent).
DataSourcePropertystringnullDataProperty name on DataContext to bind to. Used by RefreshBinding().
LinkedPropertystringnullDataLinked property name for advanced binding scenarios.
OldvalueobjectnullDataPrevious Checked value before the last SetValue call. Read-only. Tracks state transitions.

Methods

MethodVisibilitySignatureDescription
UseCheckmarkIconspublicvoid UseCheckmarkIcons()Sets OnIconName="check", OffIconName="close". Convenience preset.
UsePowerIconspublicvoid UsePowerIcons()Sets OnIconName="power", OffIconName="power_off".
UseLightIconspublicvoid UseLightIcons()Sets OnIconName="lightbulb", OffIconName="lightbulb_outline".
SetValuepublicvoid SetValue(object value)Sets Checked from bool, string ("true"/"1"/"on"/"yes"), or int (≠0). Tracks Oldvalue. IBeepComponent.
GetValuepublicobject GetValue()Returns current bool Checked value as object. IBeepComponent.
ClearValuepublicvoid ClearValue()Sets Checked=false, tracks Oldvalue. IBeepComponent.
HasFilterValuepublicbool HasFilterValue()Always returns true (switch has a definitive boolean value).
ToFilterpublicAppFilter ToFilter()Creates equality AppFilter: FieldName=BoundProperty, FilterValue=Checked.ToString(), Operator="=".
RefreshBindingpublicvoid RefreshBinding()Reads DataSourceProperty from DataContext via reflection and calls SetValue.
ApplyThemepublicvoid ApplyTheme()Applies SwitchFontHelpers font theme, reinitialises painter, updates metrics, invalidates.
AnimateToggleprivatevoid AnimateToggle(bool newState)Starts spring or cubic animation: sets up 16ms timer, computes steps from painter.GetAnimationDuration(), interpolates _animProgress (0.0-1.0).
UpdateMetricsprivatevoid UpdateMetrics()Sets _metrics.AnimationProgress, calls painter.CalculateLayout(this, _metrics). Called before every paint.
Toggleprivatevoid Toggle()Checked = !Checked. Called from hit-area callbacks.
SetCheckedprivatevoid SetChecked(bool)Checked = value. Called from hit-area label click callbacks.
InitializePainterprivatevoid InitializePainter()_painter = SwitchPainterFactory.CreatePainter(ControlStyle, this).
GetCurrentStateprivateSwitchState GetCurrentState()Resolves SwitchState (On/Off × Normal/Hover/Pressed/Disabled/Focused) from Checked, animProgress, Enabled, IsPressed, IsHovered, Focused.

Events

EventArgs TypeFire PointDescription
CheckedChangedEventHandlerChecked setter, after value change detectedRaised whenever the Checked property value changes. Fired before animation completes (animation starts first).

Animation System

The AnimateToggle(bool newState) method manages smooth transitions:

  • Duration: Retrieved from _painter.GetAnimationDuration() (typically ~250ms).
  • Steps: Math.Max(1, duration / 16) — one step per ~16ms frame.
  • Easing: Spring easing (EaseOutSpring) for Apple styles (iOS15, MacOSBigSur, Apple); cubic ease-out (EaseOutCubic) for all other styles.
  • Interpolation: _animProgress ranges 0.0 (Off thumb position) to 1.0 (On thumb position). For newState=false, _animProgress = 1.0f - easedT.
  • Fallback: When _painter == null, skips animation entirely — sets _checked = newState and _animProgress = targetProgress instantly.
  • Cancellation: Starting a new animation stops any current one via timer disposal before creating a new timer.
  • Spring Formula: Math.Pow(2, -10*t) * Math.Sin((t - s) * (2*PI) / p) + 1 with p=0.4, s=p/4. Produces an overshoot-and-settle effect.
  • Cubic Formula: 1 - Math.Pow(1 - t, 3). Smooth deceleration.

Painter Styles

ControlStylePainterAnimationDescription
iOS15iOSSwitchPainterSpringiOS-style pill switch with flat thumb, rounded track caps, subtle shadow.
MacOSBigSuriOSSwitchPainterSpringmacOS Big Sur style — similar to iOS but with larger proportions.
AppleiOSSwitchPainterSpringGeneric Apple-style switch appearance.
Material3Material3SwitchPainterCubicMaterial Design 3: state-layer colour, circular thumb, track with opacity states.
Fluent2Fluent2SwitchPainterCubicMicrosoft Fluent 2: rounded rectangle thumb, precise colour transitions.
(default)MinimalSwitchPainterCubicClean minimal style with flat colours and simple capsule shape.

The factory is in SwitchPainterFactory.CreatePainter(BeepControlStyle style, BeepSwitch owner). Painters implement ISwitchPainter with five methods: CalculateLayout, PaintTrack, PaintThumb, PaintLabels, and GetAnimationDuration.

Drag-to-Toggle

When DragToToggleEnabled = true, the user can drag the thumb to change state:

  1. OnMouseDown: If the click is within _metrics.ThumbCurrentRect, sets _dragging = true and records _dragStartX.
  2. OnMouseMove: While dragging, computes currentDistance = e.X - _dragStartX, then progress = currentDistance / totalDistance (totalDistance = ThumbOnRect.X - ThumbOffRect.X). Adjusts _animProgress by adding/subtracting progress from the current state. Clamps _animProgress to [0.0, 1.0]. Calls UpdateMetrics() and Invalidate() on every mouse move for real-time visual feedback.
  3. OnMouseUp: If _animProgress > 0.5f, sets Checked = true; otherwise Checked = false. If the state didn't change, calls AnimateToggle(_checked) to snap the thumb back into place with animation.

This provides smooth, physics-feel dragging on a linear track, similar to mobile OS toggle switches.

Theming

BeepSwitch theme integration is handled through:

  • SwitchFontHelpers.ApplyFontTheme(ControlStyle) — applies the correct font for the current BeepControlStyle from the active theme.
  • SwitchPainterFactory.CreatePainter(ControlStyle, this) — re-creates the painter to match the current ControlStyle, which reflects the theme.
  • SwitchThemeHelpers.GetFocusRingColor(theme, useThemeColors) — resolves the focus ring colour from the current theme. Returns Color.Transparent if no focus ring should be drawn.
  • UpdateMetrics() — recalculates all layout rectangles (track, thumb positions, label rects) from the new painter and control dimensions.
  • Invalidate() — forces a complete repaint after theme changes.

The track path is generated by CreateTrackPath() which adapts to the current Orientation. In Horizontal mode, arc radii equal TrackHeight / 2 (horizontal pill shape). In Vertical mode, arc radii equal TrackWidth / 2 (vertical pill shape).

The SwitchMetrics model (in Models/SwitchMetrics.cs) stores: TrackRect, ThumbOnRect, ThumbOffRect, ThumbCurrentRect, OnLabelRect, OffLabelRect, AnimationProgress, TrackHeight, TrackWidth.

Examples

Basic Toggle Switch

var toggle = new BeepSwitch
{
    Checked = false, OnLabel = "Enabled", OffLabel = "Disabled",
    Width = 120, Height = 50
};
toggle.CheckedChanged += (s, e) =>
    Console.WriteLine($"Switch: {toggle.Checked}");

Vertical Orientation

var vert = new BeepSwitch
{
    Orientation = SwitchOrientation.Vertical,
    OnLabel = "ON", OffLabel = "OFF",
    Width = 50, Height = 120
};

Icon Library Presets

var iconSwitch = new BeepSwitch { Width = 60, Height = 40 };
iconSwitch.UseCheckmarkIcons(); // check + close
iconSwitch.UsePowerIcons();     // power + power_off
iconSwitch.UseLightIcons();     // lightbulb + lightbulb_outline

Custom Icons from Library

var custom = new BeepSwitch
{
    OnIconName = "volume_up", OffIconName = "volume_mute",
    Width = 60, Height = 40
};

Data Binding

var bound = new BeepSwitch { BoundProperty = "Checked", DataSourceProperty = "IsActive" };
bound.SetValue(true);
bool val = (bool)bound.GetValue();
bound.ClearValue();
bound.RefreshBinding();

Drag-to-Toggle

var drag = new BeepSwitch
{
    DragToToggleEnabled = true, // default
    OnLabel = "Slide", OffLabel = "Slide",
    Width = 150, Height = 50
};

Custom Image as Track Background

var img = new BeepSwitch
{
    OnImagePath = @"C:\img\on_bg.png",
    OffImagePath = @"C:\img\off_bg.png",
    Width = 120, Height = 50
};

Setting from String Values

var switch2 = new BeepSwitch();
switch2.SetValue("true");  // checked
switch2.SetValue("1");     // checked
switch2.SetValue("yes");   // checked
switch2.SetValue("on");    // checked
switch2.SetValue(0);       // unchecked
switch2.SetValue(1);       // checked

Layout Metrics System

The SwitchMetrics model stores all calculated layout rectangles and is updated before every paint via UpdateMetrics():

  • TrackRect — the capsule track area, inset by padding from the client rectangle.
  • ThumbOffRect — the thumb's position when the switch is off (0.0 progress).
  • ThumbOnRect — the thumb's position when the switch is on (1.0 progress).
  • ThumbCurrentRect — linearly interpolated between Off and On based on AnimationProgress. Used for hit-testing and rendering.
  • OnLabelRect / OffLabelRect — label bounding boxes at the track ends.
  • AnimationProgress — current 0.0-1.0 float, set by animation or drag.
  • TrackHeight / TrackWidth — dimension helpers for path generation.

The track path (CreateTrackPath()) generates a horizontal pill (arc radius = TrackHeight/2) or vertical pill (arc radius = TrackWidth/2) depending on Orientation.

Architecture

FileLinesResponsibility
BeepSwitch.cs~252Main class (partial), legacy drawing methods (DrawHorizontalSwitch_Legacy, DrawVerticalSwitch_Legacy, GetCapsulePath_Legacy), Dispose (disposes _onBeepImage, _offBeepImage). Note: all modern fields/properties/methods are in the partial files below.
BeepSwitch.Core.cs~101Core: all private fields (_checked, _orientation, _onLabel, _offLabel, image paths, BeepImage instances, _onIconName, _offIconName, SwitchMetrics _metrics, ISwitchPainter _painter, Timer _animTimer, _animProgress, _animating, _dragging, _dragStartX, _dragToToggleEnabled), constructor (sets ControlStyles, default size 120x50, initialises BeepImage instances, calls InitializePainter, defaults to iOS15 style), InitializePainter.
BeepSwitch.Properties.cs~233Properties: Checked (triggers AnimateToggle + OnCheckedChanged), Orientation, OnLabel, OffLabel, OnImagePath, OffImagePath, OnIconName, OffIconName, DragToToggleEnabled, BoundProperty, DataSourceProperty, LinkedProperty. Icon convenience methods (UseCheckmarkIcons, UsePowerIcons, UseLightIcons). Event raiser OnCheckedChanged.
BeepSwitch.Drawing.cs~138Drawing: DrawContent, Draw, Paint (central: UpdateMetrics, CreateTrackPath, painter.PaintTrack/PaintThumb/PaintLabels, DrawFocusRing), CreateTrackPath (horizontal/vertical pill path generation), GetCurrentState (SwitchState resolution), DrawFocusRing.
BeepSwitch.Interaction.cs~149Input: OnMouseClick, OnMouseDown (drag start), OnMouseMove (drag tracking or cursor change), OnMouseUp (commit drag or snap-back), OnMouseLeave (reset cursor), IsInputKey (Space/Enter/Left/Right), OnKeyDown (Space/Enter toggle, arrows directional set), OnEnabledChanged, OnGotFocus, OnLostFocus.
BeepSwitch.DataBinding.cs~111Data: SetValue (bool/string/int coercion, tracks Oldvalue), GetValue, ClearValue, HasFilterValue (always true), ToFilter (AppFilter), RefreshBinding (reflection from DataContext), Oldvalue property.
BeepSwitch.Layout.cs~58Layout: OnSizeChanged, UpdateMetricsAndHitAreas, RegisterHitAreas (track, thumb, onLabel, offLabel), Toggle, SetChecked.
BeepSwitch.Animation.cs~94Animation: AnimateToggle, EaseOutCubic, EaseOutSpring, UpdateMetrics.
BeepSwitch.Theme.cs~26Theme: ApplyTheme (SwitchFontHelpers, reinitialise painter, update metrics, invalidate).
Helpers/ISwitchPainter.cs~30Interface: CalculateLayout, PaintTrack, PaintThumb, PaintLabels, GetAnimationDuration.
Helpers/SwitchPainterFactory.cs~40Factory: static CreatePainter(BeepControlStyle, BeepSwitch).
Helpers/Painters/iOSSwitchPainter.cs~100iOS style: pill track, flat thumb, spring animation.
Helpers/Painters/Material3SwitchPainter.cs~80Material3: state-layer colour, opacity transitions.
Helpers/Painters/Fluent2SwitchPainter.cs~80Fluent2: rounded-rect thumb, precise colours.
Helpers/Painters/MinimalSwitchPainter.cs~80Minimal: clean, flat colours.
Helpers/SwitchThemeHelpers.cs~30Focus ring colour resolution.
Helpers/SwitchFontHelpers.cs~30Font theme application.
Helpers/SwitchStyleHelpers.cs~30Style metrics.
Helpers/SwitchIconHelpers.cs~30Default icon paths.
Models/SwitchState.cs~20Enum: Off_Normal, Off_Hover, Off_Pressed, Off_Disabled, Off_Focused, On_* equivalents (10 values).
Models/SwitchMetrics.cs~30Model: TrackRect, ThumbOn/Off/CurrentRect, On/OffLabelRect, AnimationProgress, TrackHeight/Width.
Models/SwitchOrientation.cs~15Enum: Horizontal, Vertical.
Models/SwitchStyleConfig.cs~30Style configuration presets.
Models/SwitchColorConfig.cs~30Colour config per style per state.