Como fazer com que um grupo de botões de alternância atue como botões de opção no WPF?

122

Eu tenho um grupo de botões que devem funcionar como botões de alternância, mas também como botões de opção, onde apenas um botão pode ser selecionado / pressionado no momento. Ele também precisa ter um estado em que nenhum dos botões seja selecionado / pressionado.

O comportamento será como a barra de ferramentas do Photoshop, onde zero ou uma das ferramentas é selecionada a qualquer momento!

Alguma idéia de como isso pode ser implementado no WPF?

code-zoop
fonte

Respostas:

38

A maneira mais fácil é estilizar uma ListBox para usar ToggleButtons em seu 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>

Em seguida, você pode usar a propriedade SelectionMode do ListBox para manipular SingleSelect vs MultiSelect.

Bryan Anderson
fonte
Obrigado por compartilhar! eu queria saber se isso é uma boa prática ... mas parece ser ok ..
GorillaApe
1
@LeeLouviere: Você gostaria de elaborar por que não? Esse não é um excelente exemplo de como usar o modelo de item?
OR Mapper
4
É um mau exemplo, porque você confunde exibição e comportamento. Você altera a exibição via placa de dados, mas ainda tem o comportamento de uma caixa de listagem e não o comportamento de um conjunto de botões de opção / alternância. A interação do teclado é estranha. Uma solução melhor seria um ItemsControl com RadioButtons, denominado ToggleButtons (consulte a outra resposta mais votada).
Zarat
Por que reinventar a roda?
Wobbles
300

Esta é a maneira mais fácil na minha opinião.

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

Aproveitar! - Pricksaw

Uday Kiran Thummalapalli
fonte
29
Essa resposta deve ser selecionada de longe. Oferece a você todos os benefícios de um botão de opção, sem as desvantagens da ligação a algum controlador. Tentei primeiro ligar para separar o conjunto de botões de rádio invisíveis, mas isso tinha sobrecarga desnecessária e acabei com um bug no qual todos os botões clicados pareciam realçados, mas não necessariamente verificados.
10898 Lee Prémio
16
Ou, se você precisar aplicar isso a todos os RadioButtons dentro de um elemento (por exemplo, a Grid), use <Grid.Resources> <Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}" /> </Grid.Resources>.
Roman Starkov
16
Uma desvantagem desse método é que você não pode desmarcar um botão de opção clicando no botão marcado.
214 Julien
2
O estilo personalizado pode ser usado nesse botão de alternância de alguma forma?
Wondra
2
@wondra Você pode atribuir apenas um estilo a um FrameworkElement. No entanto, você pode adaptar solução romkyns e herdar a partir do 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 mim, divirta-se!

RoKK
fonte
Eu sei que essa não é a questão. Mas o que fazer se o botão se comportar como os botões de opção em que um sempre deve ser selecionado?
Karsten
Para a minha resposta à minha própria pergunta: ler a resposta por Uday Kiran :-)
Karsten
Bom, isso funciona como o mesmo botão de alternância. Clique para verificar e clique no mesmo botão para desmarcar. e uma vez que eu coloquei todos os RadioButtons em um mesmo grupo, eles agem como botões de opção. Obrigado RoKK !!!
mili 29/05
2
É uma boa solução. No entanto, quando temos um evento. será chamado duas vezes.
Khiem-Kim Ho Xuan
Se adicionar um conteúdo que não seja uma string simples, recebo um "Elemento já é filho de outro elemento". erro. Existe uma maneira de contornar isso?
Tobias Hoefer
5

você sempre pode usar um evento genérico no Click do ToggleButton que define todos os ToggleButton.IsChecked em um controle de grupo (Grid, WrapPanel, ...) como false com a ajuda do VisualTreeHelper; depois verifique novamente o remetente. Ou algo assim.

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

você pode colocar uma grade com botões de opção e criar botões como modelos para botões de opção. do que apenas remover programaticamente, verifique se você não deseja que os botões sejam alternados

Arsen Mkrtchyan
fonte
Mas, usando os botões de opção, há uma maneira de desmarcar todos os botões? Depois de selecionado, não vejo uma maneira fácil de desmarcá-lo!
code-Zoop
RadioButton tem uma propriedade IsChecked, você pode configurá-lo para false no código quando você precisa
Arsen Mkrtchyan
2

Você também pode tentar System.Windows.Controls.Primitives.ToggleButton

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

Em seguida, escreva um código na IsCheckedpropriedade para imitar o efeito do botão de opção

 private void btnTest_Checked(object sender, RoutedEventArgs e)
 {
     btn2.IsChecked = false;
     btn3.IsChecked = false;
 }
Jojo Sardez
fonte
Isso não é muito genérico e reutilizável ... por exemplo, não é utilizável dentro de ListBoxItems.
ANeves 14/05
0

Eu fiz isso para RibbonToggleButtons, mas talvez seja o mesmo para ToggleButtons regulares.

Liguei o IsChecked para cada botão a um valor de enum "mode" usando EnumToBooleanConverter daqui Como vincular RadioButtons a um enum? (Especifique o valor de enum para este botão usando o ConverterParameter. Você deve ter um valor de enum para cada botão)

Para evitar desmarcar um botão que já esteja marcado, coloque isso no seu código para o evento Click de cada um dos 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
fonte
0

Para ajudar pessoas como Julian e eu (dois minutos atrás ...). Você pode derivar RadioButtondesse tipo.

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

Então, você pode usá-lo como Uday Kiran sugeriu ...

<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 apenas um ToggleButtonseja de Checkedcada vez e também permite o cancelamento da verificação.

Prince Owen
fonte
0

Peguei algumas respostas e adicionei um código extra. Agora você pode ter diferentes grupos de botões de alternância que agem como um botão de alternância:

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

E os diferentes grupos de botões de opção que se parecem com botões de alternância:

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