kentforth
kentforth

Reputation: 517

How to disable all toggle buttons except one that clicked?

I'm making an application using C# and WPF

I have 8 toggle buttons.

When I click one of the buttons others should be disabled so I can't click it except one is activated. When I click this button again others should be enabled

Styles.xaml:

<!--Toggle Button-->
<Style x:Key="ToggleButton" TargetType="{x:Type ToggleButton}">
    <Setter Property="Background" Value="#535353"/>
    <Setter Property="BorderThickness" Value="0"/>
    <Setter Property="MinHeight" Value="30"/>        
    <Setter Property="Grid.Column" Value="1"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ToggleButton}">
                <Border Background="{TemplateBinding Background}"
                        BorderThickness="0">
                </Border>                    
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

MainWindow.xaml:

<ToggleButton x:Name="CategoryToggle1"
              Grid.Row="1"                      
              Style="{StaticResource ToggleButton}"
              Checked="ShowOptions"
              Unchecked="HideOptions" />

How Can I achieve this by code and XAML?

I want to do it like in this video:

Video

Upvotes: 1

Views: 2039

Answers (3)

geriwald
geriwald

Reputation: 360

The behavior you need is very specific.

I don't know how, but i got here trying to make a toggle button behave like a radio button. Your answer was enlightning.

For what it's worth, here's how you would do that :

Resource :

<Style x:Key='RadioToggle' TargetType='RadioButton'
       BasedOn='{StaticResource {x:Type ToggleButton}}' />

Control :

<RadioButton Content='RadioToggle1' IsChecked='True' 
             Style='{StaticResource RadioToggle}' 
             GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle2' 
             Style='{StaticResource RadioToggle}'
             GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle3'
             Style='{StaticResource RadioToggle}'
             GroupName="RadioToggleGroup" />

Upvotes: 0

kentforth
kentforth

Reputation: 517

Thank you for comments guys, I found another solution.

MainWindow.XAML:

 <RadioButton x:Name="CategoryToggle1"
                  Grid.Row="1" 
                 Grid.Column="1"
                 Style="{StaticResource RadioButton}"
                 GroupName="ToggleButtonsGroup"
                 Checked="OpenOptions"
                 Unchecked="HideOptions"/>

Styles.xaml:

<!--Radio Button-->
<Style TargetType="RadioButton"
       x:Key="RadioButton"
       BasedOn="{StaticResource {x:Type ToggleButton}}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

<!--Toggle Button-->
<Style TargetType="{x:Type ToggleButton}">
    <Setter Property="Background" Value="#535353" />        
    <Setter Property="MinHeight" Value="30" />
    <Setter Property="Grid.Column" Value="1" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ToggleButton">
                <Border Background="{TemplateBinding Background}"
                        BorderThickness="0">
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter  Property="Opacity" Value="0.5" />
                    </Trigger>
                    <Trigger Property="IsEnabled" Value="true">
                        <Setter  Property="Opacity" Value="1" />
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

MainWindow.cs:

private void OpenOptions(object sender, RoutedEventArgs e){
RadioButton radioButton = sender as RadioButton;
        radioButton.IsChecked = true;

        //Disable all option buttons except one that active
        MyGrid.Children.OfType<RadioButton>().Where(rb => rb != radioButton && 
    rb.GroupName == radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = false);
}

    private void HideOptions(object sender, RoutedEventArgs e)
    {
        RadioButton radioButton = sender as RadioButton;

        MyGrid.Children.OfType<RadioButton>().Where(rb => rb.GroupName == 
     radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = true);
}

Upvotes: 1

Corentin Pane
Corentin Pane

Reputation: 4943

Using Click events of each ToggleButton

One way you could do it is by giving a name to all your ToggleButtons, hook-up to their Click event and manually uncheck others in the code-behind:

XAML

<StackPanel Orientation="Vertical">
    <StackPanel.Resources>
        <Style TargetType="ToggleButton">
            <Style.Triggers>
                <Trigger Property="IsChecked" Value="False">
                    <Setter Property="MaxWidth" Value="15"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </StackPanel.Resources>
    <StackPanel Orientation="Horizontal">
        <ToggleButton x:Name="button1" Content="Button 1" Click="button1_Click"/>
        <TextBlock Text="Line 1"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <ToggleButton x:Name="button2" Content="Button 2" Click="button2_Click"/>
        <TextBlock Text="Line 2"/>
    </StackPanel>
    <StackPanel Orientation="Horizontal">
        <ToggleButton x:Name="button3" Content="Button 3" Click="button3_Click"/>
        <TextBlock Text="Line 3"/>
    </StackPanel>
</StackPanel>

Code-behind

private void button1_Click(object sender, RoutedEventArgs e) {
    if (button1.IsChecked == true) {
        button2.IsChecked = false;
        button3.IsChecked = false;
    }
}

private void button2_Click(object sender, RoutedEventArgs e) {
    if (button2.IsChecked == true) {
        button1.IsChecked = false;
        button3.IsChecked = false;
    }
}

private void button3_Click(object sender, RoutedEventArgs e) {
    if (button3.IsChecked == true) {
        button1.IsChecked = false;
        button2.IsChecked = false;
    }
}

This method is tedious, error-prone, requires code-behind and is not very scalable.

Binding IsChecked properties to a collection of bool with one true at a time.

Another way you could go (still by using code-behind) is to define a collection of boolean values and bind each ToggleButton.IsChecked on one of the bool in the collection, and ensure that the collection only contains at most one true at a time:

<StackPanel Orientation="Horizontal">
    <ToggleButton x:Name="button1" Content="Button 1" IsChecked="{Binding [0]}"/>
    <TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
    <ToggleButton x:Name="button2" Content="Button 2" IsChecked="{Binding [1]}"/>
    <TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
    <ToggleButton x:Name="button3" Content="Button 3" IsChecked="{Binding [2]}"/>
    <TextBlock Text="Line 3"/>
</StackPanel>

Code-behind

public partial class MainWindow : Window {

    public MainWindow() {
        InitializeComponent();
        ObservableCollection<bool> states = new ObservableCollection<bool> { false, false, false };
        states.CollectionChanged += States_CollectionChanged;
        DataContext = states;
    }

    private void States_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
        var collection = sender as ObservableCollection<bool>;
        if (e.Action == NotifyCollectionChangedAction.Replace) {
            if ((bool)e.NewItems[0]) {
                for (int i = 0; i < collection.Count; i++) {
                    if (e.NewStartingIndex != i) {
                        collection[i] = false;
                    }
                }
            }
        }
    }
}

Again, this uses code-behind and not the view model but at least it is easier to add rows.

Upvotes: 0

Related Questions