code-zoop
code-zoop

Reputation: 7368

How to get a group of toggle buttons to act like radio buttons in WPF?

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?

Upvotes: 133

Views: 91107

Answers (10)

heapoverflow
heapoverflow

Reputation: 105

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.

Upvotes: 0

Strawberryshrub
Strawberryshrub

Reputation: 3389

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}}">

Upvotes: 0

Prince Owen
Prince Owen

Reputation: 1505

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.

Upvotes: 0

Simon F
Simon F

Reputation: 1319

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; 
            }
        }
    }

Upvotes: 0

Uday Kiran Thummalapalli
Uday Kiran Thummalapalli

Reputation: 3407

This is easiest way in my opinion.

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

Enjoy! -- Pricksaw

Upvotes: 329

RoKK
RoKK

Reputation: 618

<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!

Upvotes: 33

Sam
Sam

Reputation: 1707

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;
    }

Upvotes: 5

Bryan Anderson
Bryan Anderson

Reputation: 16129

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.

Upvotes: 37

Jojo Sardez
Jojo Sardez

Reputation: 8568

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;
 }

Upvotes: 2

Arsen Mkrtchyan
Arsen Mkrtchyan

Reputation: 50722

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

Upvotes: 4

Related Questions