Controls
CheckBox
CheckBox is the standard independent toggle control in InkkSlinger. It derives from ToggleButton, exposes a nullable IsChecked state, and supports both the stock app theme template and an owner-drawn fallback when no template is applied.
Quick start
Use CheckBox for choices that can be enabled or disabled independently. Start with Text and IsChecked, then add events, commands, or templates only when the interaction requires them.
Minimal checkbox
<CheckBox Text="Enable telemetry" />
Bind the checked state
<CheckBox Text="Remember window layout"
IsChecked="{Binding RememberLayout}" />
Handle checked and unchecked transitions
<CheckBox Text="Receive updates"
Checked="OnReceiveUpdatesChecked"
Unchecked="OnReceiveUpdatesUnchecked" />
namespace InkkSlinger;
public partial class PreferencesView : UserControl
{
public PreferencesView()
{
InitializeComponent();
}
private void OnReceiveUpdatesChecked(object? sender, RoutedSimpleEventArgs args)
{
// Enable update delivery here.
}
private void OnReceiveUpdatesUnchecked(object? sender, RoutedSimpleEventArgs args)
{
// Disable update delivery here.
}
}
Enable three-state toggling
<CheckBox Text="Use inherited permissions"
IsThreeState="True"
IsChecked="{x:Null}" />
Default recommendation: use Text plus a bound IsChecked value for ordinary settings and checklist rows. That matches the current built-in behavior most directly.
Checked state model
CheckBox inherits its state machine from ToggleButton. The state is stored in nullable bool? IsChecked, so the control can represent checked, unchecked, and optional indeterminate states.
Two-state mode
With the default IsThreeState="False", clicks cycle between false and true.
Three-state mode
With IsThreeState="True", clicks cycle false -> true -> null -> false.
IsChecked="True"represents the checked state.IsChecked="False"represents the unchecked state.IsChecked="{x:Null}"represents the indeterminate state when three-state mode is enabled.Checked,Unchecked, andIndeterminateare bubbling routed events raised when the value changes.OnClicktoggles the state first, then raises the inherited button click behavior and command execution path.
<StackPanel>
<CheckBox Text="Draft" IsChecked="False" Margin="0,0,0,8" />
<CheckBox Text="Published" IsChecked="True" Margin="0,0,0,8" />
<CheckBox Text="Inherited"
IsThreeState="True"
IsChecked="{x:Null}" />
</StackPanel>
Use three-state mode only when null has a real meaning in your model, such as "inherit", "mixed selection", or "not decided yet".
Text, content, and commands
CheckBox inherits the text/content contract from Button. In practice, Text is the simplest path, while Content matters when a template uses a ContentPresenter.
Use Text first
In the owner-drawn fallback path, CheckBox renders the caption from Text. This is also mirrored into templated content when the template uses a ContentPresenter and you have not set explicit Content.
Use Content for custom templates
If a template needs arbitrary content, set Content explicitly. Once explicit content is set, later Text changes no longer override it.
Textis the safest end-user API for ordinary checkbox captions.Contentis most useful in templates built aroundContentPresenter.Command,CommandParameter, andCommandTargetare inherited fromButton.- The command path runs after the checked state is toggled.
Command binding example
<CheckBox Text="Accept license terms"
IsChecked="{Binding AcceptedTerms}"
Command="{Binding SaveConsentCommand}"
CommandParameter="{Binding AcceptedTerms}" />
Template-friendly content example
<CheckBox>
<CheckBox.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Enable HDR" Margin="0,0,8,0" />
<TextBlock Text="(restart required)" Foreground="#A0A0A0" />
</StackPanel>
</CheckBox.Content>
</CheckBox>
If you use explicit content like that, make sure your template actually presents it.
Styling and templating
Themed applications typically style CheckBox through a ControlTemplate. The stock App.xml template in this repo uses a horizontal layout with a 20x20 border glyph and a ContentPresenter for the caption.
Stock app theme
The app-level style sets Foreground, FontSize, and Cursor, then replaces the visual tree with a template. Hover changes the border color, checked changes both border and fill to the orange theme color, and disabled updates the caption and border colors.
Hit testing
The stock template marks the glyph border and caption presenter as IsHitTestVisible="False" so the entire checkbox surface can still toggle normally.
Indeterminate visuals
The stock App.xml template defines explicit visuals for checked and unchecked states only. If you use three-state mode and need a distinct null-state appearance, extend the template with an additional trigger and visual.
Template based on the stock pattern
<Style x:Key="SettingsCheckBoxStyle" TargetType="{x:Type CheckBox}">
<Setter Property="Foreground" Value="#F0F0F0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Border x:Name="Glyph"
Width="20"
Height="20"
IsHitTestVisible="False"
Background="#2A2A2A"
BorderBrush="#3F3F3F"
BorderThickness="2">
<Path x:Name="CheckMark"
Data="M 3,8 L 7,12 L 14,4"
Stroke="#F0F0F0"
StrokeThickness="2"
Margin="1"
Visibility="Collapsed" />
</Border>
<ContentPresenter Margin="8,0,0,0"
IsHitTestVisible="False"
RecognizesAccessKey="True"
VerticalAlignment="Center" />
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Glyph" Property="BorderBrush" Value="#FF8C00" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="Glyph" Property="Background" Value="#FF8C00" />
<Setter TargetName="Glyph" Property="BorderBrush" Value="#FF8C00" />
<Setter TargetName="CheckMark" Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Template rule: if you want clicking the label or icon area to toggle the checkbox, keep the decorative template children non-hit-testable unless they truly need direct interaction.
Layout and rendering
CheckBox has two distinct visual modes. Which one you see depends on whether a template is applied.
Themed or templated mode
When app resources or a local style apply a ControlTemplate, the template owns measurement, arrangement, and rendering. The built-in owner-drawn checkbox glyph is skipped once a template root exists.
Owner-drawn fallback mode
When no template is present, CheckBox measures and renders its own square glyph plus caption text. This path is implemented directly in CheckBox.cs.
Owner-drawn fallback behavior
- The fallback glyph is a 14x14 square with 8 pixels of spacing before the caption text.
- Desired width includes padding, glyph width, spacing when text exists, and measured text width.
- Desired height is the larger of glyph height or text height, plus vertical padding.
TextWrappingaffects text measurement and drawing when the available width is constrained.- Checked state draws a filled inner square inside the glyph.
- Indeterminate state draws a horizontal dash inside the glyph.
- Disabled state uses muted border, fill, and text colors.
<StackPanel Width="220">
<CheckBox Text="Use owner-drawn fallback with wrapped text when no template is applied."
TextWrapping="Wrap"
Padding="6" />
</StackPanel>
That example only uses the fallback layout when no template replaces the control visuals. In a templated app, the template controls the final appearance.
Property and event reference
State and events
IsChecked: nullable checked state stored as bool?.
IsThreeState: enables the indeterminate null state in the click cycle.
Checked: bubbling routed event raised when IsChecked becomes true.
Unchecked: bubbling routed event raised when IsChecked becomes false.
Indeterminate: bubbling routed event raised when IsChecked becomes null.
Text, visuals, and commands
Text: plain caption text for the fallback render path and the source for mirrored template content when explicit Content is not set.
TextWrapping: controls whether caption text is measured and drawn as one line or wrapped lines.
Foreground, Background, BorderBrush, Padding: styling inputs used directly by fallback rendering or forwarded into a template through bindings.
Command, CommandParameter, CommandTarget: inherited action surface from Button.
IsEnabled: inherited enabled state, used by both theme triggers and the fallback render path.
Patterns and examples
Settings panel checkbox
<StackPanel>
<TextBlock Text="General" Margin="0,0,0,8" />
<CheckBox Text="Launch on startup"
IsChecked="{Binding LaunchOnStartup}"
Margin="0,0,0,6" />
<CheckBox Text="Minimize to tray"
IsChecked="{Binding MinimizeToTray}" />
</StackPanel>
Checklist row with bound state
<CheckBox Text="{Binding Title}"
IsChecked="{Binding IsComplete}"
Margin="0,4,0,4" />
Command-driven consent flow
<CheckBox Text="I have reviewed the migration summary"
IsChecked="{Binding HasReviewedSummary}"
Command="{Binding RefreshContinueStateCommand}" />
Access key example
<StackPanel>
<AccessText Text="_Telemetry" TargetName="TelemetryCheckBox" />
<CheckBox x:Name="TelemetryCheckBox"
Text="Enable telemetry"
IsChecked="{Binding EnableTelemetry}" />
</StackPanel>
Three-state usage with a custom visual note
<CheckBox Text="Use project default"
IsThreeState="True"
IsChecked="{Binding EffectiveSetting}" />
If you need a visible indeterminate glyph in a template, add an additional trigger for the null state and draw a dash, mixed-fill block, or alternate accent. The stock App.xml theme in this repo does not add that visual for you.
Notes and pitfalls
Use CheckBox for independent choices. If only one option in a group can be active, use RadioButton instead.
The stock app theme has no dedicated indeterminate visual. Three-state logic is supported by the control, but the default App.xml template only styles checked and unchecked explicitly.
Template children can accidentally steal input. Decorative glyphs, paths, and caption presenters should usually set IsHitTestVisible="False" so the checkbox toggles when the user clicks anywhere on the row.
Fallback and themed visuals are different contracts. The framework guarantees the toggle logic and routed events. The exact glyph appearance depends on whether a template is active.