CheckBox & Radio Group
Toggle controls with label, icon, and group support
Namespace: Beep.Skia.Controls — Inherits: CheckBox → MaterialControl → SkiaComponent
Table of Contents
Overview
The CheckBox, CheckBoxGroup, and RadioGroup controls provide Material Design 3.0 toggle selection, rendered entirely with SkiaSharp. CheckBox is an independent toggle with a square checkmark icon that animates between checked and unchecked states. CheckBoxGroup manages a collection of checkboxes with multi-select support. RadioGroup enforces single-selection among a set of radio buttons with circular indicators.
All toggle controls follow MD3 design: the check/radio icon animates with a 200ms easing curve, ripple effects emanate from the pointer contact point, and state layers provide hover/focus/pressed feedback. Labels are rendered using BodyLarge typography with configurable text positioning relative to the icon.
Animated Check/Uncheck
The checkmark and radio fill animate with a smooth scale+fade transition managed by the Material motion system.
Indeterminate State
CheckBox supports a third state (indeterminate) drawn as a horizontal dash, useful for parent checkboxes with mixed child states.
Group Coordination
RadioGroup enforces mutual exclusion. CheckBoxGroup tracks collective state with SelectedIndices.
Keyboard Navigation
Tab to focus, Space to toggle. Arrow keys navigate between items in a group.
CheckBox Properties
| Property | Type | Description |
|---|---|---|
IsChecked |
bool? |
The toggle state. true = checked, false = unchecked, null = indeterminate (dash icon). Default is false. |
Text |
string |
Label text displayed to the right of the check box icon. Can be null for icon-only checkboxes. |
CheckBoxStyle |
CheckBoxStyle |
Enum: Square (default, standard checkbox) or Toggle (switch-style with a pill-shaped thumb). |
IconSize |
float |
Size of the check box icon square. Default is 18f per MD3 spec. |
LabelPosition |
LabelPosition |
Enum: Right (default) or Left. Controls whether the label text appears to the right or left of the icon. |
IsThreeState |
bool |
When true, clicking cycles through false → true → null (indeterminate) → false. Default is false. |
IsHovered |
bool |
Read-only. Whether the mouse is over the touch target (icon + label area). |
CheckBoxStyle.Toggle (Switch) Properties
When CheckBoxStyle is set to Toggle, the control renders as a Material Design switch with additional properties:
| Property | Type | Description |
|---|---|---|
ToggleTrackWidth | float | Width of the switch track. Default is 52f. |
ToggleTrackHeight | float | Height of the switch track. Default is 32f. |
ToggleThumbSize | float | Diameter of the circular thumb. Default is 24f. |
CheckBoxGroup Properties
CheckBoxGroup is a container control that manages a collection of CheckBox children:
| Property | Type | Description |
|---|---|---|
Items |
List<CheckBox> |
The child checkboxes managed by this group. Each maintains its own IsChecked state independently. |
SelectedIndices |
HashSet<int> |
Read-only. The zero-based indices of currently checked items. Updated automatically when any child checkbox toggles. |
LayoutDirection |
LayoutDirection |
Enum: Vertical (default) or Horizontal. Controls how child checkboxes are arranged. |
ItemSpacing |
float |
Gap between child checkboxes. Default is 8f. |
RadioGroup Properties
RadioGroup manages a mutually-exclusive set of radio buttons. Unlike CheckBoxGroup, only one item can be selected at a time.
| Property | Type | Description |
|---|---|---|
Items |
List<RadioItem> |
The radio button items. Each RadioItem has Text, Value, and optional Icon properties. The radio circle is drawn by the group, not per-item. |
SelectedIndex |
int |
Index of the selected radio button. -1 when nothing is selected. Setting this updates all child radio circles. |
SelectedValue |
object |
Read-only. The Value property of the selected RadioItem, or null. |
RadioDiameter |
float |
Diameter of the radio circle. Default is 20f. |
LayoutDirection |
LayoutDirection |
Enum: Vertical (default) or Horizontal. |
Events
| Event | Owner | Signature | Description |
|---|---|---|---|
CheckedChanged |
CheckBox |
EventHandler<bool?> |
Raised when IsChecked changes. The argument is the new value (true, false, or null for indeterminate). |
SelectedIndicesChanged |
CheckBoxGroup |
EventHandler<HashSet<int>> |
Raised when any child checkbox toggles. The argument is the updated SelectedIndices set. |
SelectedIndexChanged |
RadioGroup |
EventHandler<int> |
Raised when the selected radio button changes. The argument is the new SelectedIndex. |
Code Example
Basic CheckBox and Toggle Switch
var darkModeCheck = new CheckBox
{
Text = "Enable Dark Mode",
IsChecked = false,
X = 40, Y = 40,
Width = 200, Height = 40
};
darkModeCheck.Initialize();
darkModeCheck.CheckedChanged += (s, state) =>
Console.WriteLine($"Dark mode: {state}");
var notificationsToggle = new CheckBox
{
Text = "Push Notifications",
CheckBoxStyle = CheckBoxStyle.Toggle,
IsChecked = true,
X = 40, Y = 90,
Width = 220, Height = 40
};
notificationsToggle.Initialize();
drawingManager.AddComponent(darkModeCheck);
drawingManager.AddComponent(notificationsToggle);
drawingManager.RequestRedraw();
Three-State CheckBox (Indeterminate)
var child1 = new CheckBox { Text = "Option A", X = 0, Y = 0 };
var child2 = new CheckBox { Text = "Option B", X = 0, Y = 0 };
var child3 = new CheckBox { Text = "Option C", X = 0, Y = 0 };
var selectAll = new CheckBox
{
Text = "Select All",
IsThreeState = true,
IsChecked = false,
X = 40, Y = 150,
Width = 200, Height = 40
};
selectAll.Initialize();
selectAll.CheckedChanged += (s, state) =>
{
if (state == true || state == false)
{
child1.IsChecked = state;
child2.IsChecked = state;
child3.IsChecked = state;
}
};
Action updateParent = () =>
{
var states = new[] { child1.IsChecked, child2.IsChecked, child3.IsChecked };
if (states.All(c => c == true)) selectAll.IsChecked = true;
else if (states.All(c => c == false)) selectAll.IsChecked = false;
else selectAll.IsChecked = null;
};
child1.CheckedChanged += (s, v) => updateParent();
child2.CheckedChanged += (s, v) => updateParent();
child3.CheckedChanged += (s, v) => updateParent();
drawingManager.AddComponent(selectAll);
CheckBoxGroup with Vertical Layout
var group = new CheckBoxGroup
{
X = 300, Y = 40,
Width = 250,
LayoutDirection = LayoutDirection.Vertical,
ItemSpacing = 4f,
Items = new List<CheckBox>
{
new() { Text = "Feature A: Auto-save" },
new() { Text = "Feature B: Spell check" },
new() { Text = "Feature C: Live preview" },
new() { Text = "Feature D: Dark theme" },
new() { Text = "Feature E: Compact mode" }
}
};
foreach (var cb in group.Items) cb.Initialize();
group.Initialize();
group.SelectedIndicesChanged += (s, indices) =>
Console.WriteLine($"Selected: {string.Join(", ", indices)}");
drawingManager.AddComponent(group);
RadioGroup
var radioGroup = new RadioGroup
{
X = 300, Y = 280,
Width = 250,
LayoutDirection = LayoutDirection.Vertical,
SelectedIndex = 0,
Items = new List<RadioItem>
{
new() { Text = "Small (12px)", Value = 12 },
new() { Text = "Medium (16px)", Value = 16 },
new() { Text = "Large (20px)", Value = 20 },
new() { Text = "Extra Large (24px)", Value = 24 }
}
};
radioGroup.Initialize();
radioGroup.SelectedIndexChanged += (s, idx) =>
{
int fontSize = (int)radioGroup.SelectedValue;
Console.WriteLine($"Font size set to: {fontSize}px");
};
drawingManager.AddComponent(radioGroup);
drawingManager.RequestRedraw();
Tip
When using CheckBoxGroup or RadioGroup, add the group to the drawing manager, not individual child items. The group manages child layout, event routing, and invalidation automatically. Child coordinates are relative to the group's origin.