WPF Standard Control Demo App › ToggleButton
ToggleButton Inputs
ToggleButton is a button that stays pressed after a click and is released by the next one. CheckBox and RadioButton derive from it.
Clicks, IsThreeState, and why null looks unchecked
ToggleButton derives from ButtonBase, and both CheckBox and RadioButton derive from ToggleButton. Its state is IsChecked, a bool? that binds two-way by default. With IsThreeState="True", clicks went false → true → null → false; with the default False, false → true → false. The demo app shows three buttons starting at null, false, and true; none of them is three-state, so the one that starts at null goes to false on the first click and cannot return to null.
The default template has no visual state groups. It changes its look with a trigger on IsChecked = true only, so a button whose IsChecked is null looks the same as an unchecked one. The demo app’s three-state button looks unpressed in both states; only the text next to it shows the difference. If you use IsThreeState, give the template a look for null. UI Automation does report all three states: ToggleState was Off, On, and Indeterminate for false, true, and null.
ToggleButton has no GroupName; for mutually exclusive toggles, use RadioButton.
ClickMode and Command: what runs, and when
These are not on the demo app’s ToggleButton screen, but they are common with toggle buttons and were measured in the same way.
ClickMode="Press" toggles when the mouse button goes down, and the new state stays after it is released: IsChecked was true after the button went down and still true after it came up. It does not make a button that is active only while held.
Command runs after the state has changed. With CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}", a click from false passed True to Execute, and IsChecked was already true there. Toggling the button through UI Automation’s Toggle, as an assistive technology does, changed IsChecked but did not run the command. Do not rely on Command alone; keep the state in a bound property if it must be handled for every way the button can be toggled.
Opening a Popup with a ToggleButton
A Popup whose IsOpen is bound to IsChecked opened when the button was clicked. When the popup closed, IsChecked returned to false, because Popup.IsOpen binds two-way by default. With StaysOpen="False" and real clicks, clicking an empty area of the window closed the popup and cleared the button. Clicking the button itself while the popup was open did not close it: afterwards IsOpen and IsChecked were both still true.
| case | measured |
|---|---|
| base class / derived: CheckBox, RadioButton | ButtonBase / ToggleButton, ToggleButton |
| IsChecked: BindsTwoWayByDefault / GroupName property on ToggleButton | True / False |
| IsThreeState=False, start null: 3 clicks | null -> false -> true -> false |
| IsThreeState=False, start false: 3 clicks | false -> true -> false -> true |
| IsThreeState=False, start true: 3 clicks | true -> false -> true -> false |
| IsThreeState=True, start false: 3 clicks | false -> true -> null -> false |
| default template: VisualStateGroups / triggers on IsChecked (values) | 0 / true |
| UI Automation ToggleState | false -> Off, true -> On, null -> Indeterminate |
| ClickMode=Press: IsChecked after button down / after button up (false before) | true / true |
| Command, CommandParameter bound to its own IsChecked; clicked (OnClick) from false: in Execute | IsChecked true, parameter True |
| the same button toggled through UI Automation's Toggle: Command | not executed |
| Popup.IsOpen bound to IsChecked (Mode not set): after click / after the popup closes | IsChecked true, IsOpen True / IsChecked false |
| Popup.IsOpen: BindsTwoWayByDefault | True |
| StaysOpen=False, real clicks (IsOpen, IsChecked): button / empty area | True, true / False, false |
| button again / button while open | True, true / True, true |
States, template, ClickMode, Command, and a bound Popup. Measured on .NET 10 / Windows 11.
Trying it in the demo app

The ToggleButton page of the demo app has two sections, IsChecked and IsThreeState, each showing the value next to the button. The “Show Code” link under each section displays its XAML. The following XAML is the IsThreeState section (ToggleButtonUsageControl.xaml), with the styles and the surrounding layout left out and the namespace declarations added:
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ToggleButton x:Name="IsThreeStateTrueButton" HorizontalAlignment="Stretch"
Content="ToggleButton" IsThreeState="True" />
<TextBlock HorizontalAlignment="Center"
Text="{Binding IsChecked, ElementName=IsThreeStateTrueButton, TargetNullValue=(Null)}" />
<ToggleButton x:Name="IsThreeStateFalseButton" HorizontalAlignment="Stretch"
Content="ToggleButton" IsThreeState="False" />
<TextBlock HorizontalAlignment="Center"
Text="{Binding IsChecked, ElementName=IsThreeStateFalseButton}" />
</StackPanel>
Related controls and articles
- CheckBox — a ToggleButton drawn as a check box.
- RadioButton — a ToggleButton for one choice out of several.
- Popup — often opened and closed by a ToggleButton.
Source code and how it was measured
Every behavior on this page was measured by running it on .NET 10 / Windows 11, using ToggleButtonDemoScene in this site’s screenshot tool. State changes were made with UI Automation’s Toggle. The command was run through the button’s click handling (OnClick), and ClickMode was checked by raising the left-button events.