Как заставить группу переключателей действовать как переключатели в WPF?

122

У меня есть группа кнопок, которые должны действовать как кнопки переключения, а также как переключатели, где в текущий момент может быть выбрана / нажата только одна кнопка. Также необходимо иметь состояние, в котором ни одна из кнопок не выбрана / не нажата.

Поведение будет похоже на панель инструментов Photoshop, где в любой момент можно выбрать ноль или один из инструментов!

Есть идеи, как это можно реализовать в WPF?

Код-Zoop
источник

Ответы:

38

Самый простой способ - стилизовать ListBox, чтобы использовать ToggleButtons для его 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>

Затем вы можете использовать свойство SelectionMode ListBox для обработки SingleSelect и MultiSelect.

Брайан Андерсон
источник
Спасибо, что поделились! мне было интересно, является ли это хорошей практикой ... но, похоже, все в порядке ..
GorillaApe
1
@LeeLouviere: Не могли бы вы объяснить, почему бы и нет? Разве это не яркий пример того, как использовать шаблон элемента?
OR Mapper
4
Это плохой пример, потому что вы путаете отображение и поведение. Вы изменяете отображение через datatemplate, но у вас по-прежнему есть поведение списка, а не поведение набора переключателей / переключателей. Странное взаимодействие с клавиатурой. Лучшим решением будет ItemsControl с RadioButtons, оформленный как ToggleButtons (см. Другой ответ с наибольшим количеством голосов).
Зарат
Зачем изобретать велосипед?
Колебания
300

На мой взгляд, это самый простой способ.

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

Наслаждайтесь! - Пила

Удай Киран Туммалапалли
источник
29
Этот ответ следует выбрать однозначно. Дает вам все преимущества радиокнопки без недостатков привязки к какому-либо контроллеру. Сначала я попытался привязать к отдельному набору невидимых радиокнопок, но это привело к ненужным накладным расходам и закончилось ошибкой, когда все нажатые кнопки выглядели выделенными, но не обязательно отмеченными.
Ли Лувьер,
16
Или, если вам нужно применить это ко всем RadioButton внутри элемента (например, a Grid), используйте <Grid.Resources> <Style TargetType="RadioButton" BasedOn="{StaticResource {x:Type ToggleButton}}" /> </Grid.Resources>.
Роман Старков
16
одним из недостатков этого метода является то, что вы не можете снять отметку с радиокнопки, щелкнув отмеченную.
Жюльен
2
Можно ли каким-то образом использовать собственный стиль на такой кнопке-переключателе?
wondra
2
@wondra Вы можете назначить FrameworkElement только один стиль. Однако вы можете адаптировать решение romkyns и унаследовать его от стиля ToggleButton:<Style BasedOn="{StaticResource {x:Type ToggleButton}}" x:Key="Blubb" TargetType="RadioButton"><Setter Property="Background" Value="Yellow" /></Style>
Суиджи,
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>

у меня работает, наслаждайтесь!

Rokk
источник
Я знаю, что вопрос не в этом. Но что делать, если кнопка должна вести себя как радиокнопки, где ее всегда нужно выбирать?
Karsten
На мой собственный вопрос: прочтите ответ Удая Кирана :-)
Карстен
Хорошо, это действует ТАК ЖЕ, что и кнопка переключения. Щелкните, чтобы установить флажок, и нажмите ту же кнопку, чтобы снять флажок. и как только я помещаю все RadioButtons в одну группу, они действуют как радио-кнопки. Спасибо, RoKK !!!
mili
2
Это хорошее решение. Однако, когда у нас есть событие. он будет вызываться дважды.
Кхием-Ким Хо Суан
Если добавляется другой контент, а не простая строка, я получаю сообщение «Элемент уже является дочерним по отношению к другому элементу». ошибка. Есть ли способ обойти это?
Tobias Hoefer
5

вы всегда можете использовать универсальное событие для Click элемента ToggleButton, которое устанавливает для всех ToggleButton.IsChecked в групповом элементе управления (Grid, WrapPanel, ...) значение false с помощью VisualTreeHelper; затем еще раз проверьте отправителя. Или что-то в этом роде.

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

вы можете поместить в нее сетку с радиокнопками и создать кнопку как шаблон для raduiobuttons. чем просто программно удалить, проверьте, не хотите ли вы переключать кнопки

Арсен Мкртчян
источник
Но есть ли способ отменить выбор всех кнопок с помощью переключателей? После выбора я не вижу простого способа отменить его!
code-zoop 02
RadioButton имеет свойство IsChecked, вы можете установить для него значение false в коде, когда вам нужно,
Арсен Мкртчян
2

Вы также можете попробовать System.Windows.Controls.Primitives.ToggleButton

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

Затем напишите код для IsCheckedсвойства, чтобы имитировать эффект радиокнопки.

 private void btnTest_Checked(object sender, RoutedEventArgs e)
 {
     btn2.IsChecked = false;
     btn3.IsChecked = false;
 }
Jojo Sardez
источник
Это не очень общий и многоразовый ... например, его нельзя использовать внутри ListBoxItems.
ANeves
0

Я сделал это для RibbonToggleButtons, но, возможно, то же самое и для обычных ToggleButtons.

Я привязал IsChecked для каждой кнопки к значению перечисления "mode", используя EnumToBooleanConverter отсюда. Как привязать RadioButtons к перечислению? (Укажите значение перечисления для этой кнопки с помощью ConverterParameter. У вас должно быть одно значение перечисления для каждой кнопки)

Затем, чтобы предотвратить снятие отметки с кнопки, которая уже отмечена, поместите это в свой код для события Click для каждого из 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

Чтобы помочь людям вроде меня и Джулиана (две минуты назад ...). Вы можете извлечь из этого RadioButtonвот что.

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

Затем вы можете использовать его, как предложил Удай Киран ...

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

Этот метод позволяет только один , ToggleButtonчтобы быть Checkedв то время, и это также позволяет Непроверку.

Принц Оуэн
источник
0

Я взял несколько ответов и добавил дополнительный код. Теперь у вас могут быть разные группы кнопок-переключателей, которые действуют как одна кнопка-переключатель:

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

И различные группы переключателей, которые выглядят как кнопки переключения:

<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
источник