Comment faire en sorte qu'un groupe de boutons bascule agisse comme des boutons radio dans WPF?

122

J'ai un groupe de boutons qui devraient agir comme des boutons à bascule, mais aussi comme des boutons radio où un seul bouton peut être sélectionné / enfoncé à la fois. Il doit également avoir un état dans lequel aucun des boutons n'est sélectionné / enfoncé.

Le comportement sera un peu comme la barre d'outils Photoshop, où zéro ou l'un des outils est sélectionné à tout moment!

Une idée de la façon dont cela peut être implémenté dans WPF?

code-zoop
la source

Réponses:

38

Le moyen le plus simple est de styliser un ListBox pour utiliser ToggleButtons pour son 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>

Ensuite, vous pouvez utiliser la propriété SelectionMode de ListBox pour gérer SingleSelect vs MultiSelect.

Bryan Anderson
la source
Merci pour le partage! Je me demandais si c'était une bonne pratique ... mais ça semble aller ...
GorillaApe
1
@LeeLouviere: Souhaitez-vous préciser pourquoi pas? N'est-ce pas un excellent exemple de la façon d'utiliser le modèle d'article?
OR Mapper
4
C'est un mauvais exemple parce que vous confondez affichage et comportement. Vous modifiez l'affichage via datatemplate mais vous avez toujours le comportement d'une listbox et non le comportement d'un ensemble de boutons radio / bascule. L'interaction avec le clavier est bizarre. Une meilleure solution serait un ItemsControl avec RadioButtons, appelé ToggleButtons (voir l'autre réponse la plus votée).
Zarat
Pourquoi réinventer la roue?
Wobbles
300

C'est le moyen le plus simple à mon avis.

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

Prendre plaisir! - Pricksaw

Uday Kiran Thummalapalli
la source
29
Celui-ci devrait être sélectionné de loin. Vous offre tous les avantages d'un bouton radio sans les inconvénients de la liaison à un contrôleur. J'ai d'abord essayé de lier pour séparer l'ensemble de boutons radio invisibles, mais cela avait une surcharge inutile et s'est soldé par un bug où tous les boutons cliqués semblaient mis en évidence mais pas nécessairement cochés.
Lee Louviere
16
Ou, si vous devez appliquer ceci à tous les RadioButtons à l'intérieur d'un élément (par exemple a Grid), utilisez <Grid.Resources> <Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}" /> </Grid.Resources>.
Roman Starkov
16
un inconvénient de cette méthode est que vous ne pouvez pas décocher un bouton radio en cliquant sur un bouton coché.
Julien
2
Un style personnalisé peut-il être utilisé sur un tel bouton à bascule?
Wondra
2
@wondra Vous ne pouvez attribuer qu'un seul style à un FrameworkElement. Cependant, vous pouvez adapter la solution romkyns et hériter du style ToggleButton:<Style BasedOn="{StaticResource {x:Type ToggleButton}}" x:Key="Blubb" TargetType="RadioButton"><Setter Property="Background" Value="Yellow" /></Style>
Suigi
32
<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>

ça marche pour moi, profitez-en!

RoKK
la source
Je sais que ce n'est pas la question. Mais que faire si le bouton doit se comporter comme les boutons radio où il faut toujours en sélectionner un?
Karsten
A ma réponse ma propre question: lisez la réponse par Uday Kiran :-)
Karsten
Bien, cela agit comme MÊME que le bouton bascule. Cliquez pour vérifier et cliquez sur le même bouton pour décocher. et une fois que j'ai mis tous les RadioButtons dans un même groupe, cela agit comme des boutons Radio. Merci RoKK !!!
mili
2
C'est une bonne solution. Cependant, lorsque nous avons un événement. il sera appelé deux fois.
Khiem-Kim Ho Xuan
Si vous ajoutez un contenu autre qu'une simple chaîne, j'obtiens un "L'élément est déjà l'enfant d'un autre élément." Erreur. Y a-t-il un moyen de contourner cela?
Tobias Hoefer
5

vous pouvez toujours utiliser un événement générique sur le Click du ToggleButton qui définit tous les ToggleButton.IsChecked dans un groupcontrol (Grid, WrapPanel, ...) sur false à l'aide de VisualTreeHelper; puis revérifiez l'expéditeur. Ou quelque chose comme ça.

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
la source
4

vous pouvez mettre une grille avec des boutons radio et créer un bouton comme un modèle pour les boutons radio. que de simplement supprimer par programme, vérifiez si vous ne voulez pas que les boutons soient basculés

Arsen Mkrtchyan
la source
Mais en utilisant les boutons radio, existe-t-il un moyen de désélectionner tous les boutons? Une fois sélectionné, je ne vois pas de moyen facile de le désélectionner!
code-zoop
RadioButton a une propriété IsChecked, vous pouvez la définir sur false dans le code lorsque vous en avez besoin
Arsen Mkrtchyan
2

Vous pouvez également essayer System.Windows.Controls.Primitives.ToggleButton

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

Ensuite, écrivez du code contre la IsCheckedpropriété pour imiter l'effet du bouton radio

 private void btnTest_Checked(object sender, RoutedEventArgs e)
 {
     btn2.IsChecked = false;
     btn3.IsChecked = false;
 }
Jojo Sardez
la source
Ce n'est pas très générique et réutilisable ... par exemple, ce n'est pas utilisable dans ListBoxItems.
ANeves
0

J'ai fait cela pour RibbonToggleButtons, mais c'est peut-être la même chose pour les ToggleButtons classiques.

J'ai lié le IsChecked pour chaque bouton à une valeur d'énumération "mode" en utilisant EnumToBooleanConverter à partir d'ici Comment lier RadioButtons à une énumération? (Spécifiez la valeur d'énumération de ce bouton à l'aide de ConverterParameter. Vous devez avoir une valeur d'énumération pour chaque bouton)

Ensuite, pour éviter de décocher un bouton déjà coché, placez-le dans votre code derrière pour l'événement Click pour chacun des 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; 
            }
        }
    }
Simon F
la source
0

Pour aider des gens comme Julian et moi (il y a deux minutes ...). Vous pouvez en dériver RadioButton.

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

Ensuite, vous pouvez l'utiliser comme Uday Kiran l'a suggéré ...

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

Cette méthode ne permet qu'une seule ToggleButtond'être Checkedà la fois, et il permet également décochant.

Prince Owen
la source
0

J'ai pris quelques éléments des réponses et j'ai ajouté du code supplémentaire. Vous pouvez maintenant avoir différents groupes de boutons bascule qui agissent comme un bouton bascule:

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

Et les différents groupes de boutons radio qui ressemblent à des boutons à bascule:

<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}}">
Arbuste de paille
la source