131

I have a group of buttons that should act like toggle buttons, but also as radio buttons where only one button can be selected / pressed down at a current time. It also need to have a state where none of the buttons are selected / pressed down.

The behavior will be kind of like Photoshop toolbar, where zero or one of the tools are selected at any time!

Any idea how this can be implemented in WPF?

Andrea Antonangeli
  • 1,242
  • 1
  • 21
  • 32
code-zoop
  • 7,312
  • 8
  • 47
  • 56

10 Answers10

325

This is easiest way in my opinion.

<RadioButton Style="{StaticResource {x:Type ToggleButton}}" />

Enjoy! -- Pricksaw

  • 32
    This one should be selected answer by far. Gives you all the benefits of a radiobutton without the drawbacks of binding to some controller. I first tried binding to separate invisible radiobutton set, but that had unnecessary overhead and ended up with a bug where all the buttons clicked looked highlighted but not necessarily checked. – Lee Louviere Apr 11 '12 at 17:48
  • 20
    Or, if you need to apply this to all RadioButtons inside an element (e.g. a `Grid`), use ` `. – Roman Starkov Dec 06 '12 at 18:53
  • 18
    one disadvantage to this method is that you cannot uncheck a radio button by clicking on a checked one. – Julien Feb 25 '14 at 17:25
  • 2
    Can be custom style used on such togglebutton somehow? – wondra Jun 11 '14 at 23:28
  • 2
    @wondra You can only assign a single style to a FrameworkElement. However, you can adapt romkyns solution and inherit from the ToggleButton style: ```````` – Suigi Jun 18 '15 at 13:52
  • 2
    For WinRT/UWP: `{x:Type ...}` wont work here. But you can copy the default ToggleButton style from [here](https://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj709930.aspx?f=255&MSPPError=-2147217396) to a ResourceDictionary and use `` instead – Tobias Hoefer Jul 21 '15 at 13:15
  • 2
    @Julien If you want to "uncheck a (button) by clicking a checked one", you shouldn't be using a `RadioButton`. The whole point is: RadioButton ["Represents a button that can be selected, but not cleared, by a user"](https://msdn.microsoft.com/en-us/library/system.windows.controls.radiobutton.aspx). If you want to allow a user to uncheck it, then use a `ToggleButton`. – Disillusioned Nov 05 '15 at 06:20
  • 1
    @Craig: be that as it may, the OP has _clearly_ stated that they want the "no buttons checked" state to be possible. IMHO it is a grey area: some UIs show radio button groups with no button checked until the user clicks one, and the framework doesn't prohibit this state though it could. So it's only a short path to allowing the user to _re-enter_ that state by clicking the currently-checked button. Besides: _"If you want to allow a user to uncheck it, then use a ToggleButton"_, well...the `RadioButton` class _is_ a `ToggleButton`. So... – Peter Duniho Mar 10 '16 at 18:35
  • @LeeLouviere Yes, this answer should be the right one. – Abdulsalam Elsharif Aug 01 '18 at 10:59
  • you save my ass. – Jake Aug 25 '20 at 07:03
37

The easiest way is to style a ListBox to use ToggleButtons for its ItemTemplate

<Style TargetType="{x:Type ListBox}">
    <Setter Property="ListBox.ItemTemplate">
        <Setter.Value>
            <DataTemplate>
                <ToggleButton Content="{Binding}" 
                              IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}"
                />
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

Then you can use the SelectionMode property of the ListBox to handle SingleSelect vs MultiSelect.

Bryan Anderson
  • 15,969
  • 8
  • 68
  • 83
  • thank you for sharing! i was wondering whether this is good practice... but it seems to be ok.. – GorillaApe Oct 02 '11 at 17:46
  • 1
    @LeeLouviere: Would you care to elaborate why not? Isn't that a prime example of how to use the item template? – O. R. Mapper Oct 31 '13 at 13:30
  • 4
    It's a bad example because you confuse display and behavior. You change the display via datatemplate but you still have the behavior of a listbox and *not* the behavior of a set of radio/toggle buttons. Keyboard interaction is weird. A better solution would be an ItemsControl with RadioButtons, styled as ToggleButtons (see the other highest voted answer). – Zarat Apr 15 '15 at 10:36
  • Why reinvent the wheel? – Wobbles Sep 10 '19 at 11:43
33
<RadioButton Content="Point" >
    <RadioButton.Template>
        <ControlTemplate>
            <ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
                          Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
        </ControlTemplate>
    </RadioButton.Template>
</RadioButton>

it works for me, enjoy!

RoKK
  • 617
  • 8
  • 17
  • I know this isn't the question. But what to do if the button should behave like the radiobuttons where one must always be selected? – Karsten Jun 28 '11 at 12:46
  • To my answer my own question: read the answer by Uday Kiran :-) – Karsten Jun 28 '11 at 12:49
  • Nice, this act as SAME as Toggle button. Click to check and click on same button to uncheck. and once I put all the RadioButtons in a same group, it act like Radio buttons. Thank you RoKK !!! – mili May 29 '14 at 11:21
  • 2
    It is a good solution. However, when we have an event. it will be called twice. – Khiem-Kim Ho Xuan May 29 '15 at 06:11
  • If a add content other then a simple string i get a "Element is already the child of another element." error. Is there a way arround this? – Tobias Hoefer Jul 21 '15 at 12:49
  • This is definitely the best and simplest answer based on how the question was asked (and what I happened to be looking for so thanks!). The first solution mixes display and behavior concerns in a way inconsistent with WPF principles, and the highest ranked answer doesn't let you deselect the currently selected item. – Mike Marynowski Feb 08 '16 at 17:19
  • This is good answer by far, as I have used it. But now I am facing one issue because if this solution. if the Radiobuttons are used into Toolbar when IsOverflowOpen is true templates then these radiobuttons with same Groupname act as different groups because radiobuttins are being split into different containers.Any suggestions on this issue – Sumit Datta Jun 12 '18 at 05:53
  • I just tried this solution and the one with most upvotes. This solution *does* allow me to **deselect** the radiobutton, the answer of Uday doesn't. So shouldn't this be the accepted answer instead? – bas Sep 27 '19 at 07:46
5

you could always use a generic event on the Click of the ToggleButton that sets all ToggleButton.IsChecked in a groupcontrol(Grid, WrapPanel, ...) to false with the help of the VisualTreeHelper; then re-check the sender. Or something in the likes of that.

private void ToggleButton_Click(object sender, RoutedEventArgs e)
    {
        int childAmount = VisualTreeHelper.GetChildrenCount((sender as ToggleButton).Parent);

        ToggleButton tb;
        for (int i = 0; i < childAmount; i++)
        {
            tb = null;
            tb = VisualTreeHelper.GetChild((sender as ToggleButton).Parent, i) as ToggleButton;

            if (tb != null)
                tb.IsChecked = false;
        }

        (sender as ToggleButton).IsChecked = true;
    }
Sam
  • 1,634
  • 13
  • 21
4

you can put grid with radiobuttons in it, and create button like template for raduiobuttons. than just programmaticaly remove check if you don't want buttons to be toggled

Arsen Mkrtchyan
  • 49,896
  • 32
  • 148
  • 184
2

You can also try System.Windows.Controls.Primitives.ToggleButton

 <ToggleButton Name="btnTest" VerticalAlignment="Top">Test</ToggleButton>

Then write code against the IsChecked property to mimick the radiobutton effect

 private void btnTest_Checked(object sender, RoutedEventArgs e)
 {
     btn2.IsChecked = false;
     btn3.IsChecked = false;
 }
Jojo Sardez
  • 8,400
  • 3
  • 27
  • 38
  • This is not very generic and reusable... for example, it's not usable inside `ListBoxItem`s. – ANeves May 14 '15 at 16:47
0

I did this for RibbonToggleButtons, but maybe it's the same for regular ToggleButtons.

I bound the IsChecked for each button to a "mode" enum value using EnumToBooleanConverter from here How to bind RadioButtons to an enum? (Specify the enum value for this button using the ConverterParameter. You should have one enum value for each button)

Then to prevent unchecking a button that's already checked, put this in your code behind for the Click event for each of the RibbonToggleButtons:

    private void PreventUncheckRibbonToggleButtonOnClick ( object sender, RoutedEventArgs e ) {

        // Prevent unchecking a checked toggle button - so that one always remains checked
        // Cancel the click if you hit an already-checked button

        var button = (RibbonToggleButton)sender;
        if( button.IsChecked != null ) { // Not sure why checked can be null but that's fine, ignore it
            bool notChecked = ( ! (bool)button.IsChecked );
            if( notChecked ){ // I guess this means the click would uncheck it
                button.IsChecked = true; 
            }
        }
    }
Community
  • 1
  • 1
Simon F
  • 1,299
  • 1
  • 11
  • 14
0

To help people like Julian and me (two minutes ago...). You can derive from the RadioButton like this.

class RadioToggleButton : RadioButton
{
    protected override void OnToggle()
    {
        if (IsChecked == true) IsChecked = IsThreeState ? (bool?)null : (bool?)false;
        else IsChecked = IsChecked.HasValue;
    }
}

Then, you can use it like Uday Kiran suggested...

<Window x:Class="Sample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Sample"
    Title="MainWindow" Height="600" Width="600">
    <StackPanel>
        <local:RadioToggleButton Content="Button" Style="{StaticResource {x:Type ToggleButton}}" />
    </StackPanel>
</Window>

This method allows only one ToggleButton to be Checked at a time, and it also allows UnChecking.

Prince Owen
  • 1,225
  • 12
  • 20
0

I took a few piece of the answers and added some extra code. Now you can have different groups of toggle buttons which act like one toggle button:

<UserControl.Resources>
    <Style x:Key="GroupToggleStyle" TargetType="ToggleButton">
        <Style.Triggers>
            <MultiDataTrigger>
                <MultiDataTrigger.Conditions>
                    <Condition Binding="{Binding GroupName, RelativeSource={RelativeSource Self}}" Value="Group1"/>
                    <Condition Binding="{Binding BooleanProperty}" Value="true"/>
                </MultiDataTrigger.Conditions>
                <MultiDataTrigger.Setters>
                    <Setter Property="IsChecked" Value="true"/>
                </MultiDataTrigger.Setters>
            </MultiDataTrigger>
        </Style.Triggers>
    </Style>
</UserControl.Resources>

And the different groups of radio buttons which look like toggle buttons:

<Radio Button GroupName="Group1" Style="{StaticResource {x:Type ToggleButton}}">
<Radio Button GroupName="Group1" Style="{StaticResource {x:Type ToggleButton}}">
<Radio Button GroupName="Group2" Style="{StaticResource {x:Type ToggleButton}}">
<Radio Button GroupName="Group3" Style="{StaticResource {x:Type ToggleButton}}">
Strawberryshrub
  • 3,301
  • 2
  • 11
  • 20
0

One simplistic implementation could be where you maintain a flag in your code behind such as:

ToggleButton _CurrentlyCheckedButton;

Then assign a single Checked event handler to all your context ToggleButtons:

private void ToggleButton_Checked(object sender, RoutedEventArgs e)
{
   if (_CurrentlyCheckedButton != null)
      _CurrentlyCheckedButton.IsChecked = false;

   _CurrentlyCheckedButton = (sender as ToggleButton);
}

And, a single Unchecked event handler:

private void ToggleButton_Unchecked(object sender, RoutedEventArgs e)
{
    if (_CurrentlyCheckedButton == (sender as ToggleButton))
       _CurrentlyCheckedButton = null;
}

This way you can have the 'zero or one' selection you desire.

heapoverflow
  • 85
  • 1
  • 8