¿Cómo hacer que un grupo de botones de alternancia actúe como botones de opción en WPF?

122

Tengo un grupo de botones que deberían actuar como botones de alternancia, pero también como botones de radio donde solo se puede seleccionar / presionar un botón en un momento actual. También debe tener un estado en el que ninguno de los botones esté seleccionado / presionado.

El comportamiento será una especie de barra de herramientas de Photoshop, donde se selecciona cero o una de las herramientas en cualquier momento.

¿Alguna idea de cómo se puede implementar esto en WPF?

código-zoop
fuente

Respuestas:

38

La forma más fácil es diseñar un ListBox para usar ToggleButtons para su 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>

Luego, puede usar la propiedad SelectionMode del ListBox para manejar SingleSelect vs MultiSelect.

Bryan Anderson
fuente
¡gracias por compartir!
Me
1
@LeeLouviere: ¿Le importaría explicar por qué no? ¿No es ese un excelente ejemplo de cómo usar la plantilla de elementos?
OR Mapper
4
Es un mal ejemplo porque confundes visualización y comportamiento. Cambia la pantalla a través de la plantilla de datos, pero aún tiene el comportamiento de un cuadro de lista y no el comportamiento de un conjunto de botones de radio / alternar. La interacción con el teclado es extraña. Una mejor solución sería un ItemsControl con RadioButtons, con el estilo ToggleButtons (ver la otra respuesta más votada).
Zarat
¿Por qué reinventar la rueda?
Wobbles
300

En mi opinión, esta es la forma más sencilla.

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

¡Disfrutar! - sierra de aguja

Uday Kiran Thummalapalli
fuente
29
Esta debería ser la respuesta seleccionada con diferencia. Le brinda todos los beneficios de un botón de radio sin los inconvenientes de vincularse a algún controlador. Primero intenté enlazar a un conjunto de botones de radio invisibles separados, pero eso tenía una sobrecarga innecesaria y terminó con un error en el que todos los botones en los que se hizo clic parecían resaltados pero no necesariamente marcados.
Lee Louviere
16
O, si necesita aplicar esto a todos los RadioButtons dentro de un elemento (por ejemplo, a Grid), use <Grid.Resources> <Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}" /> </Grid.Resources>.
Roman Starkov
16
Una desventaja de este método es que no puede desmarcar un botón de radio haciendo clic en uno marcado.
Julien
2
¿Se puede usar un estilo personalizado en tal botón de alternancia de alguna manera?
wondra
2
@wondra Solo puede asignar un estilo a un FrameworkElement. Sin embargo, puede adaptar la solución romkyns y heredar del estilo 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>

funciona para mi, disfruta!

RoKK
fuente
Sé que esta no es la cuestión. Pero, ¿qué hacer si el botón debe comportarse como los botones de radio donde siempre se debe seleccionar uno?
Karsten
A mi respuesta a mi propia pregunta: lea la respuesta de Uday Kiran :-)
Karsten
Bien, esto actúa como el MISMO que el botón Alternar. Haga clic para marcar y haga clic en el mismo botón para desmarcar. y una vez que coloco todos los RadioButtons en un mismo grupo, actúan como botones de radio. Gracias RoKK !!!
mili
2
Es una buena solucion. Sin embargo, cuando tenemos un evento. se llamará dos veces.
Khiem-Kim Ho Xuan
Si agrego contenido diferente a una cadena simple, obtengo un "El elemento ya es hijo de otro elemento". error. ¿Hay alguna forma de evitar esto?
Tobias Hoefer
5

siempre puede usar un evento genérico en el clic del ToggleButton que establece todos los ToggleButton.IsChecked en un control de grupo (Grid, WrapPanel, ...) en falso con la ayuda de VisualTreeHelper; luego vuelva a verificar el remitente. O algo parecido a eso.

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

puede poner una cuadrícula con botones de radio y crear un botón como plantilla para botones de radio. que simplemente eliminar programáticamente la verificación si no desea que los botones se alternen

Arsen Mkrtchyan
fuente
Pero mediante el uso de botones de opción, ¿hay alguna forma de anular la selección de todos los botones? Una vez seleccionado, no veo una manera fácil de deseleccionarlo.
code-zoop
RadioButton tiene una propiedad IsChecked, puede configurarlo en falso en el código cuando lo necesite
Arsen Mkrtchyan
2

También puedes probar System.Windows.Controls.Primitives.ToggleButton

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

Luego, escriba el código contra la IsCheckedpropiedad para imitar el efecto del botón de radio

 private void btnTest_Checked(object sender, RoutedEventArgs e)
 {
     btn2.IsChecked = false;
     btn3.IsChecked = false;
 }
Jojo Sardez
fuente
Esto no es muy genérico y reutilizable ... por ejemplo, no se puede usar dentro de ListBoxItems.
ANEVES
0

Hice esto para RibbonToggleButtons, pero tal vez sea lo mismo para los ToggleButtons normales.

Vinculé el IsChecked para cada botón a un valor de enumeración de "modo" usando EnumToBooleanConverter desde aquí ¿Cómo vincular RadioButtons a una enumeración? (Especifique el valor de enumeración para este botón mediante ConverterParameter. Debe tener un valor de enumeración para cada botón)

Luego, para evitar desmarcar un botón que ya está marcado, coloque esto en su código detrás del evento Click para cada uno de los 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; 
            }
        }
    }
Simón F
fuente
0

Para ayudar a gente como Julian y yo (hace dos minutos ...). Puede derivar de RadioButtonesto.

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

Entonces, puedes usarlo como sugirió Uday Kiran ...

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

Este método permite que solo haya uno ToggleButtona la Checkedvez, y también permite UnChecking.

Príncipe Owen
fuente
0

Tomé algunas respuestas y agregué código adicional. Ahora puede tener diferentes grupos de botones de alternancia que actúan como un solo botón de alternancia:

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

Y los diferentes grupos de botones de radio que parecen botones de alternancia:

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