Come fare in modo che un gruppo di pulsanti di attivazione / disattivazione agisca come pulsanti di opzione in WPF?


122

Ho un gruppo di pulsanti che dovrebbero agire come pulsanti di attivazione / disattivazione, ma anche come pulsanti di opzione in cui è possibile selezionare / premere un solo pulsante all'ora corrente. Inoltre, deve avere uno stato in cui nessuno dei pulsanti è selezionato / premuto.

Il comportamento sarà un po 'come la barra degli strumenti di Photoshop, dove nessuno o uno degli strumenti sono selezionati in qualsiasi momento!

Qualche idea su come questo possa essere implementato in WPF?

Risposte:


38

Il modo più semplice è applicare uno stile a un ListBox per utilizzare ToggleButtons per il relativo 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>

Quindi puoi usare la proprietà SelectionMode del ListBox per gestire SingleSelect vs MultiSelect.


Grazie per aver condiviso! mi chiedevo se questa sia una buona pratica ... ma sembra essere ok ..
GorillaApe

1
@LeeLouviere: ti andrebbe di approfondire perché no? Non è un ottimo esempio di come utilizzare il modello di articolo?
OR Mapper

4
È un cattivo esempio perché confondi display e comportamento. Si cambia la visualizzazione tramite datatemplate ma si ha ancora il comportamento di una casella di riepilogo e non il comportamento di un insieme di pulsanti di opzione / attivazione / disattivazione. L'interazione con la tastiera è strana. Una soluzione migliore sarebbe un ItemsControl con RadioButtons, in stile ToggleButtons (vedi l'altra risposta con il voto più alto).
Zarat

Perché reinventare la ruota?
Wobbles

300

Questo è il modo più semplice secondo me.

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

Godere! - Seghetto


29
Questa dovrebbe essere di gran lunga la risposta selezionata. Offre tutti i vantaggi di un pulsante di opzione senza gli svantaggi del collegamento a un controller. Ho provato per la prima volta a eseguire il binding per separare il set di pulsanti radio invisibili, ma aveva un sovraccarico non necessario e si è concluso con un bug in cui tutti i pulsanti cliccati sembravano evidenziati ma non necessariamente selezionati.
Lee Louviere

16
Oppure, se hai bisogno di applicarlo a tutti i RadioButtons all'interno di un elemento (es. A Grid), usa <Grid.Resources> <Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}" /> </Grid.Resources>.
Roman Starkov

16
uno svantaggio di questo metodo è che non è possibile deselezionare un pulsante di opzione facendo clic su uno selezionato.
Julien

2
Lo stile personalizzato può essere utilizzato in qualche modo su tale pulsante?
wondra

2
@wondra È possibile assegnare un solo stile a un FrameworkElement. Tuttavia, puoi adattare la soluzione romkyns ed ereditare dallo stile 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>

per me funziona, divertiti!


So che non è questa la domanda. Ma cosa fare se il pulsante deve comportarsi come i pulsanti radio di cui uno deve essere sempre selezionato?
Karsten

Alla mia risposta alla mia domanda: leggi la risposta di Uday Kiran :-)
Karsten

Bene, questo agisce come lo stesso pulsante di attivazione / disattivazione. Fare clic per selezionare e fare clic sullo stesso pulsante per deselezionare. e una volta che ho messo tutti i RadioButtons nello stesso gruppo, si comporta come i Radio Button. Grazie RoKK !!!
mili

2
È una buona soluzione. Tuttavia, quando abbiamo un evento. sarà chiamato due volte.
Khiem-Kim Ho Xuan

Se aggiungo un contenuto diverso da una semplice stringa ottengo un "Elemento è già figlio di un altro elemento." errore. C'è un modo per aggirare questo?
Tobias Hoefer

5

è sempre possibile utilizzare un evento generico sul clic del ToggleButton che imposta tutti i ToggleButton.IsChecked in un groupcontrol (Grid, WrapPanel, ...) su false con l'aiuto di VisualTreeHelper; quindi ricontrolla il mittente. O qualcosa del genere.

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

4

puoi inserire una griglia con i pulsanti radio e creare un pulsante come un modello per i pulsanti radio. piuttosto che rimuovere il controllo a livello di programmazione se non si desidera che i pulsanti vengano attivati


Ma usando i pulsanti di opzione c'è un modo per deselezionare tutti i pulsanti? Una volta selezionato, non vedo un modo semplice per deselezionarlo!
code-zoop

RadioButton ha una proprietà IsChecked, puoi impostarla su false nel codice quando ne hai bisogno
Arsen Mkrtchyan

2

Puoi anche provare System.Windows.Controls.Primitives.ToggleButton

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

Quindi scrivi il codice sulla IsCheckedproprietà per imitare l'effetto del pulsante di opzione

 private void btnTest_Checked(object sender, RoutedEventArgs e)
 {
     btn2.IsChecked = false;
     btn3.IsChecked = false;
 }

Questo non è molto generico e riutilizzabile ... per esempio, non è utilizzabile all'interno di ListBoxItems.
ANeves

0

L'ho fatto per RibbonToggleButtons, ma forse è lo stesso per i normali ToggleButtons.

Ho associato IsChecked per ogni pulsante a un valore di enumerazione "mode" utilizzando EnumToBooleanConverter da qui. Come associare RadioButtons a un enum? (Specifica il valore enum per questo pulsante utilizzando ConverterParameter. Dovresti avere un valore enum per ogni pulsante)

Quindi, per evitare di deselezionare un pulsante già selezionato, inseriscilo nel codice dietro per l'evento Click per ciascuno dei 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; 
            }
        }
    }

0

Per aiutare persone come Julian e me (due minuti fa ...). Puoi derivare da RadioButtonquesto.

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

Quindi, puoi usarlo come suggerito da 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>

Questo metodo consente ToggleButtondi essere solo uno alla Checkedvolta e consente anche di deselezionare.


0

Ho preso alcune risposte e ho aggiunto del codice extra. Ora puoi avere diversi gruppi di pulsanti di attivazione / disattivazione che agiscono come un pulsante di attivazione / disattivazione:

<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 i diversi gruppi di pulsanti di opzione che sembrano pulsanti di attivazione / disattivazione:

<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}}">
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.