Bagaimana cara mendapatkan sekelompok tombol sakelar untuk bertindak seperti tombol radio di WPF?

122

Saya memiliki sekelompok tombol yang seharusnya berfungsi seperti tombol sakelar, tetapi juga sebagai tombol radio di mana hanya satu tombol yang dapat dipilih / ditekan saat ini. Ini juga harus memiliki status di mana tidak ada tombol yang dipilih / ditekan.

Perilakunya akan seperti toolbar Photoshop, di mana nol atau salah satu alat dipilih kapan saja!

Adakah ide bagaimana ini dapat diterapkan di WPF?

kode-zoop
sumber

Jawaban:

38

Cara termudah adalah dengan mendesain ListBox menggunakan ToggleButtons untuk ItemTemplate-nya

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

Kemudian Anda dapat menggunakan properti SelectionMode dari ListBox untuk menangani SingleSelect vs MultiSelect.

Bryan Anderson
sumber
Terima kasih sudah berbagi! saya bertanya-tanya apakah ini praktik yang baik ... tetapi tampaknya baik-baik saja ..
GorillaApe
1
@LeeLouviere: Maukah Anda menjelaskan mengapa tidak? Bukankah itu contoh utama bagaimana menggunakan template item?
ATAU Mapper
4
Ini contoh yang buruk karena Anda bingung antara tampilan dan perilaku. Anda mengubah tampilan melalui datatemplate tetapi Anda masih memiliki perilaku listbox dan bukan perilaku sekumpulan tombol radio / toggle. Interaksi keyboard itu aneh. Solusi yang lebih baik adalah ItemsControl dengan RadioButtons, dengan gaya ToggleButtons (lihat jawaban pilihan tertinggi lainnya).
Zarat
Mengapa menemukan kembali roda?
Goyah
300

Ini cara termudah menurut saya.

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

Nikmati! - Pricksaw

Uday Kiran Thummalapalli
sumber
29
Jawaban yang satu ini harus dipilih sejauh ini. Memberi Anda semua manfaat dari tombol radio tanpa kekurangan pengikatan ke beberapa pengontrol. Saya pertama kali mencoba mengikat untuk memisahkan kumpulan tombol radio yang tidak terlihat, tetapi itu memiliki overhead yang tidak perlu dan berakhir dengan bug di mana semua tombol yang diklik tampak disorot tetapi tidak perlu dicentang.
Lee Louviere
16
Atau, jika Anda perlu menerapkan ini ke semua RadioButtons di dalam elemen (mis. A Grid), gunakan <Grid.Resources> <Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}" /> </Grid.Resources>.
Roman Starkov
16
satu kelemahan dari metode ini adalah Anda tidak dapat menghapus centang pada tombol radio dengan mengklik yang sudah dicentang.
Julien
2
Dapatkah gaya kustom digunakan pada tombol tombol seperti itu?
keajaiban
2
@wondra Anda hanya dapat menetapkan satu gaya ke FrameworkElement. Namun, Anda dapat mengadaptasi solusi romkyns dan mewarisi dari gaya 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>

itu bekerja untuk saya, selamat menikmati!

RoKK
sumber
Saya tahu ini bukan pertanyaannya. Tetapi apa yang harus dilakukan jika tombol harus berperilaku seperti tombol radio di mana seseorang harus selalu dipilih?
Karsten
Untuk jawaban saya, pertanyaan saya sendiri: baca jawaban oleh Uday Kiran :-)
Karsten
Bagus, ini berfungsi sama dengan tombol Toggle. Klik untuk memeriksa dan klik tombol yang sama untuk menghapus centang. dan setelah saya memasukkan semua RadioButtons ke dalam grup yang sama, itu berfungsi seperti tombol Radio. Terima kasih RoKK !!!
mili
2
Itu adalah solusi yang bagus. Namun, saat kita ada acara. itu akan dipanggil dua kali.
Khiem-Kim Ho Xuan
Jika menambahkan konten lain maka string sederhana saya mendapatkan "Elemen sudah menjadi anak dari elemen lain." kesalahan. Apakah ada cara untuk mengatasi ini?
Tobias Hoefer
5

Anda selalu bisa menggunakan acara umum di Klik dari ToggleButton yang menetapkan semua ToggleButton.IsChecked di groupcontrol (Grid, WrapPanel, ...) menjadi false dengan bantuan VisualTreeHelper; lalu periksa kembali pengirimnya. Atau sesuatu yang seperti itu.

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

Anda dapat meletakkan grid dengan radiobuttons di dalamnya, dan membuat tombol seperti template untuk raduiobuttons. daripada hanya menghapus centang secara terprogram jika Anda tidak ingin tombol diubah

Arsen Mkrtchyan
sumber
Tetapi dengan menggunakan tombol radio, adakah cara untuk membatalkan pilihan semua tombol? Setelah dipilih, saya tidak melihat cara mudah untuk membatalkannya!
kode-zoop
RadioButton memiliki properti IsChecked, Anda dapat menyetelnya ke false dalam kode saat Anda membutuhkannya
Arsen Mkrtchyan
2

Anda juga bisa mencoba System.Windows.Controls.Primitives.ToggleButton

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

Kemudian tulis kode pada IsCheckedproperti untuk meniru efek tombol radio

 private void btnTest_Checked(object sender, RoutedEventArgs e)
 {
     btn2.IsChecked = false;
     btn3.IsChecked = false;
 }
Jojo Sardez
sumber
Ini tidak terlalu umum dan dapat digunakan kembali ... misalnya, tidak dapat digunakan di dalam ListBoxItems.
ANeves
0

Saya melakukan ini untuk RibbonToggleButtons, tapi mungkin itu sama untuk ToggleButtons biasa.

Saya mengikat IsChecked untuk setiap tombol ke nilai enum "mode" menggunakan EnumToBooleanConverter dari sini Bagaimana mengikat RadioButtons ke enum? (Tentukan nilai enum untuk tombol ini menggunakan ConverterParameter. Anda harus memiliki satu nilai enum untuk setiap tombol)

Kemudian untuk mencegah tidak mencentang tombol yang sudah dicentang, letakkan ini di kode Anda di belakang untuk acara Klik untuk setiap 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.
sumber
0

Untuk membantu orang-orang seperti Julian dan saya (dua menit yang lalu ...). Anda bisa mendapatkan dari RadioButtonseperti ini.

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

Kemudian, Anda dapat menggunakannya seperti yang disarankan 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>

Metode ini memungkinkan hanya satu ToggleButtonuntuk menjadi Checkedpada satu waktu, dan juga memungkinkan centang.

Pangeran Owen
sumber
0

Saya mengambil beberapa bagian dari jawaban dan menambahkan beberapa kode tambahan. Sekarang Anda dapat memiliki grup tombol sakelar berbeda yang berfungsi seperti satu tombol sakelar:

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

Dan berbagai grup tombol radio yang terlihat seperti tombol sakelar:

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