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, with the control list on the left and the first section, IsChecked

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>

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.

View ToggleButton source code on GitHub →