Подтвердить что ты не робот

Простое WPF RadioButton Binding?

Каков самый простой способ привязать группу из трех радиообъектов к свойству типа int для значений 1, 2 или 3?

4b9b3361

Ответ 1

На самом деле, использование такого конвертера нарушает двустороннее связывание, плюс, как я сказал выше, вы не можете использовать это и с перечислениями. Лучший способ сделать это - использовать простой стиль для ListBox, например так:

Примечание. Вопреки тому, что DrWPF.com указал в своем примере, не помещайте ContentPresenter в RadioButton, иначе, если вы добавите элемент с содержимым, таким как кнопка или что-то еще, вы не сможете установить фокус или взаимодействовать с ним., Эта техника решает это. Кроме того, вам нужно обрабатывать серую текст, а также удалять поля на ярлыках, иначе он будет отображаться неправильно. Этот стиль подходит и для вас.

<Style x:Key="RadioButtonListItem" TargetType="{x:Type ListBoxItem}" >

    <Setter Property="Template">
        <Setter.Value>

            <ControlTemplate TargetType="ListBoxItem">

                <DockPanel LastChildFill="True" Background="{TemplateBinding Background}" HorizontalAlignment="Stretch" VerticalAlignment="Center" >

                    <RadioButton IsChecked="{TemplateBinding IsSelected}" Focusable="False" IsHitTestVisible="False" VerticalAlignment="Center" Margin="0,0,4,0" />

                    <ContentPresenter
                        Content             = "{TemplateBinding ContentControl.Content}"
                        ContentTemplate     = "{TemplateBinding ContentControl.ContentTemplate}"
                        ContentStringFormat = "{TemplateBinding ContentControl.ContentStringFormat}"
                        HorizontalAlignment = "{TemplateBinding Control.HorizontalContentAlignment}"
                        VerticalAlignment   = "{TemplateBinding Control.VerticalContentAlignment}"
                        SnapsToDevicePixels = "{TemplateBinding UIElement.SnapsToDevicePixels}" />

                </DockPanel>

            </ControlTemplate>

        </Setter.Value>

    </Setter>

</Style>

<Style x:Key="RadioButtonList" TargetType="ListBox">

    <Style.Resources>
        <Style TargetType="Label">
            <Setter Property="Padding" Value="0" />
        </Style>
    </Style.Resources>

    <Setter Property="BorderThickness" Value="0" />
    <Setter Property="Background"      Value="Transparent" />

    <Setter Property="ItemContainerStyle" Value="{StaticResource RadioButtonListItem}" />

    <Setter Property="Control.Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type ListBox}">
                <ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>

    <Style.Triggers>
        <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextBlock.Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
        </Trigger>
    </Style.Triggers>

</Style>

<Style x:Key="HorizontalRadioButtonList" BasedOn="{StaticResource RadioButtonList}" TargetType="ListBox">
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel Background="Transparent" Orientation="Horizontal" />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

Теперь у вас есть внешний вид переключателей, но вы можете выполнить двустороннее связывание и использовать перечисление. Вот как...

<ListBox Style="{StaticResource RadioButtonList}"
    SelectedValue="{Binding SomeVal}"
    SelectedValuePath="Tag">

    <ListBoxItem Tag="{x:Static l:MyEnum.SomeOption}"     >Some option</ListBoxItem>
    <ListBoxItem Tag="{x:Static l:MyEnum.SomeOtherOption}">Some other option</ListBoxItem>
    <ListBoxItem Tag="{x:Static l:MyEnum.YetAnother}"     >Yet another option</ListBoxItem>

</ListBox>

Кроме того, поскольку мы явно отделили стиль, который отслеживает ListBoxItem, а не помещаем его в строку, опять же, как показали другие примеры, теперь вы можете создать новый стиль для него, чтобы настраивать элементы для каждого элемента, такие как интервал. (Это не будет работать, если вы просто попытаетесь настроить таргетинг на ListBoxItem, так как стиль ключа переопределяет общие цели управления.)

Вот пример размещения поля 6 выше и ниже каждого элемента. (Обратите внимание, как вы должны явно применять стиль через свойство ItemContainerStyle, а не просто нацеливать его на ListBoxItem в разделе ресурсов ListBox по причине, указанной выше.)

<Window.Resources>
    <Style x:Key="SpacedRadioButtonListItem" TargetType="ListBoxItem" BasedOn="{StaticResource RadioButtonListItem}">
        <Setter Property="Margin" Value="0,6" />
    </Style>
</Window.Resources>

<ListBox Style="{StaticResource RadioButtonList}"
    ItemContainerStyle="{StaticResource SpacedRadioButtonListItem}"
    SelectedValue="{Binding SomeVal}"
    SelectedValuePath="Tag">

    <ListBoxItem Tag="{x:Static l:MyEnum.SomeOption}"     >Some option</ListBoxItem>
    <ListBoxItem Tag="{x:Static l:MyEnum.SomeOtherOption}">Some other option</ListBoxItem>
    <ListBoxItem Tag="{x:Static l:MyEnum.YetAnother}"     >Ter another option</ListBoxItem>

</ListBox>

Ответ 2

Я придумал простое решение.

У меня есть класс model.cs с:

private int _isSuccess;
public int IsSuccess { get { return _isSuccess; } set { _isSuccess = value; } }

У меня есть файл Window1.xaml.cs с DataContext, установленным в model.cs. Xaml содержит радиообъекты:

<RadioButton IsChecked="{Binding Path=IsSuccess, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=1}" Content="one" />
<RadioButton IsChecked="{Binding Path=IsSuccess, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=2}" Content="two" />
<RadioButton IsChecked="{Binding Path=IsSuccess, Converter={StaticResource radioBoolToIntConverter}, ConverterParameter=3}" Content="three" />

Вот мой конвертер:

public class RadioBoolToIntConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        int integer = (int)value;
        if (integer==int.Parse(parameter.ToString()))
            return true;
        else
            return false;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return parameter;
    }
}

И, конечно, в ресурсах Window1:

<Window.Resources>
    <local:RadioBoolToIntConverter x:Key="radioBoolToIntConverter" />
</Window.Resources>

Ответ 3

Я очень удивлен, что никто не придумал такого решения, чтобы связать его с массивом bool. Он может быть не самым чистым, но его можно использовать очень легко:

private bool[] _modeArray = new bool[] { true, false, false};
public bool[] ModeArray
{
    get { return _modeArray ; }
}
public int SelectedMode
{
    get { return Array.IndexOf(_modeArray, true); }
}

в XAML:

<RadioButton GroupName="Mode" IsChecked="{Binding Path=ModeArray[0], Mode=TwoWay}"/>
<RadioButton GroupName="Mode" IsChecked="{Binding Path=ModeArray[1], Mode=TwoWay}"/>
<RadioButton GroupName="Mode" IsChecked="{Binding Path=ModeArray[2], Mode=TwoWay}"/>

ПРИМЕЧАНИЕ. Вам не требуется двусторонняя привязка, если вы не хотите, чтобы по умолчанию был установлен флажок. Связывание TwoWay является самым большим недостатком этого решения.

Плюсы:

  • Не нужно писать код
  • Нет необходимости в дополнительном классе (конвертер IValue)
  • Нет необходимости в дополнительных перечислениях
  • не требует привязки bizzare
  • простой и понятный
  • не нарушает MVVM (хех, по крайней мере, я надеюсь)

Ответ 4

Я знаю, что это просрочено, но у меня есть альтернативное решение, которое легче и проще. Выведите класс из System.Windows.Controls.RadioButton и объявите два свойства зависимостей RadioValue и RadioBinding. Затем в коде класса переопределите OnChecked и установите значение свойства RadioBinding в значение свойства RadioValue. В другом направлении ловушка переходит в свойство RadioBinding с помощью обратного вызова, а если новое значение равно значению свойства RadioValue, установите для его свойства IsChecked значение true.

Здесь код:

public class MyRadioButton : RadioButton
{
    public object RadioValue
    {
        get { return (object)GetValue(RadioValueProperty); }
        set { SetValue(RadioValueProperty, value); }
    }

    // Using a DependencyProperty as the backing store for RadioValue.
       This enables animation, styling, binding, etc...
    public static readonly DependencyProperty RadioValueProperty =
        DependencyProperty.Register(
            "RadioValue", 
            typeof(object), 
            typeof(MyRadioButton), 
            new UIPropertyMetadata(null));

    public object RadioBinding
    {
        get { return (object)GetValue(RadioBindingProperty); }
        set { SetValue(RadioBindingProperty, value); }
    }

    // Using a DependencyProperty as the backing store for RadioBinding.
       This enables animation, styling, binding, etc...
    public static readonly DependencyProperty RadioBindingProperty =
        DependencyProperty.Register(
            "RadioBinding", 
            typeof(object), 
            typeof(MyRadioButton), 
            new FrameworkPropertyMetadata(
                null, 
                FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, 
                OnRadioBindingChanged));

    private static void OnRadioBindingChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        MyRadioButton rb = (MyRadioButton)d;
        if (rb.RadioValue.Equals(e.NewValue))
            rb.SetCurrentValue(RadioButton.IsCheckedProperty, true);
    }

    protected override void OnChecked(RoutedEventArgs e)
    {
        base.OnChecked(e);
        SetCurrentValue(RadioBindingProperty, RadioValue);
    }
}

Использование XAML:

<my:MyRadioButton GroupName="grp1" Content="Value 1"
    RadioValue="val1" RadioBinding="{Binding SelectedValue}"/>
<my:MyRadioButton GroupName="grp1" Content="Value 2"
    RadioValue="val2" RadioBinding="{Binding SelectedValue}"/>
<my:MyRadioButton GroupName="grp1" Content="Value 3"
    RadioValue="val3" RadioBinding="{Binding SelectedValue}"/>
<my:MyRadioButton GroupName="grp1" Content="Value 4"
    RadioValue="val4" RadioBinding="{Binding SelectedValue}"/>

Надеюсь, что кто-то найдет это полезным после всего этого времени:)

Ответ 5

Иногда это можно решить в модели следующим образом: Предположим, что у вас есть 3 булевых свойства OptionA, OptionB, OptionC.

XAML:

<RadioButton IsChecked="{Binding OptionA}"/>
<RadioButton IsChecked="{Binding OptionB}"/>
<RadioButton IsChecked="{Binding OptionC}"/>

CODE:

private bool _optionA;
public bool OptionA
{
    get { return _optionA; }
    set
    {
        _optionA = value;
        if( _optionA )
        {
             this.OptionB= false;
             this.OptionC = false;
        }
    }
}

private bool _optionB;
public bool OptionB
{
    get { return _optionB; }
    set
    {
        _optionB = value;
        if( _optionB )
        {
            this.OptionA= false;
            this.OptionC = false;
        }
    }
}

private bool _optionC;
public bool OptionC
{
    get { return _optionC; }
    set
    {
        _optionC = value;
        if( _optionC )
        {
            this.OptionA= false;
            this.OptionB = false;
        }
    }
}

Вы получаете идею. Не самая чистая вещь, но легко.

Ответ 6

Этот пример может показаться немного длинным, но его намерение должно быть достаточно ясным.

Он использует 3 булевых свойства в вызываемой модели ViewModel, FlagForValue1, FlagForValue2 и FlagForValue3. Каждое из этих 3 свойств поддерживается одним частным полем под названием _intValue.

3 радиокнопки вида (xaml) привязаны к соответствующему свойству Flag в модели представления. Это означает, что переключатель, отображающий значение "1", привязан к свойству FlagForValue1 bool в модели представления и двум другим соответствующим образом.

При настройке одного из свойств в модели представления (например, FlagForValue1) важно также повысить свойства измененных событий для двух других свойств (например, FlagForValue2 и FlagForValue3), поэтому пользовательский интерфейс (WPF INotifyPropertyChanged) можно правильно выбрать/отменить выбор каждого переключателя.

    private int _intValue;

    public bool FlagForValue1
    {
        get
        {
            return (_intValue == 1) ? true : false;
        }
        set
        {
            _intValue = 1;
            RaisePropertyChanged("FlagForValue1");
            RaisePropertyChanged("FlagForValue2");
            RaisePropertyChanged("FlagForValue3");
        }
    }

    public bool FlagForValue2
    {
        get
        {
            return (_intValue == 2) ? true : false;
        }
        set
        {
            _intValue = 2;
            RaisePropertyChanged("FlagForValue1");
            RaisePropertyChanged("FlagForValue2");
            RaisePropertyChanged("FlagForValue3");
        }
    }

    public bool FlagForValue3
    {
        get
        {
            return (_intValue == 3) ? true : false;
        }
        set
        {
            _intValue = 3;
            RaisePropertyChanged("FlagForValue1");
            RaisePropertyChanged("FlagForValue2");
            RaisePropertyChanged("FlagForValue3");
        }
    }

xaml выглядит так:

                <RadioButton GroupName="Search" IsChecked="{Binding Path=FlagForValue1, Mode=TwoWay}"
                             >Value 1</RadioButton>

                <RadioButton GroupName="Search" IsChecked="{Binding Path=FlagForValue2, Mode=TwoWay}"
                             >Value 2</RadioButton>

                <RadioButton GroupName="Search" IsChecked="{Binding Path=FlagForValue3, Mode=TwoWay}"
                             >Value 3</RadioButton>

Ответ 7

Я придумал решение, использующее Binding.DoNothing возвращаемое конвертером, которое не нарушает двустороннюю привязку.

public class EnumToCheckedConverter : IValueConverter
{
    public Type Type { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value.GetType() == Type)
        {
            try
            {
                var parameterFlag = Enum.Parse(Type, parameter as string);

                if (Equals(parameterFlag, value))
                {
                    return true;
                }
            }
            catch (ArgumentNullException)
            {
                return false;
            }
            catch (ArgumentException)
            {
                throw new NotSupportedException();
            }

            return false;
        }
        else if (value == null)
        {
            return false;
        }

        throw new NotSupportedException();
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value is bool check)
        {
            if (check)
            {
                try
                {
                    return Enum.Parse(Type, parameter as string);
                }
                catch(ArgumentNullException)
                {
                    return Binding.DoNothing;
                }
                catch(ArgumentException)
                {
                    return Binding.DoNothing;
                }
            }

            return Binding.DoNothing;
        }

        throw new NotSupportedException();
    }
}

Использование:

<converters:EnumToCheckedConverter x:Key="SourceConverter" Type="{x:Type monitor:VariableValueSource}" />

Привязки радиокнопок:

<RadioButton GroupName="ValueSource" 
             IsChecked="{Binding Source, Converter={StaticResource SourceConverter}, ConverterParameter=Function}">Function</RadioButton>

Ответ 8

Я создал прикрепленное свойство на основе Aviad Answer, которое не требует создания нового класса

public static class RadioButtonHelper
{
    [AttachedPropertyBrowsableForType(typeof(RadioButton))]
    public static object GetRadioValue(DependencyObject obj) => obj.GetValue(RadioValueProperty);
    public static void SetRadioValue(DependencyObject obj, object value) => obj.SetValue(RadioValueProperty, value);
    public static readonly DependencyProperty RadioValueProperty =
        DependencyProperty.RegisterAttached("RadioValue", typeof(object), typeof(RadioButtonHelper), new PropertyMetadata(new PropertyChangedCallback(OnRadioValueChanged)));

    private static void OnRadioValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is RadioButton rb)
        {
            rb.Checked -= OnChecked;
            rb.Checked += OnChecked;
        }
    }

    public static void OnChecked(object sender, RoutedEventArgs e)
    {
        if (sender is RadioButton rb)
        {
            rb.SetCurrentValue(RadioBindingProperty, rb.GetValue(RadioValueProperty));
        }
    }

    [AttachedPropertyBrowsableForType(typeof(RadioButton))]
    public static object GetRadioBinding(DependencyObject obj) => obj.GetValue(RadioBindingProperty);
    public static void SetRadioBinding(DependencyObject obj, object value) => obj.SetValue(RadioBindingProperty, value);

    public static readonly DependencyProperty RadioBindingProperty =
        DependencyProperty.RegisterAttached("RadioBinding", typeof(object), typeof(RadioButtonHelper), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnRadioBindingChanged)));

    private static void OnRadioBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is RadioButton rb && rb.GetValue(RadioValueProperty).Equals(e.NewValue))
        {
            rb.SetCurrentValue(RadioButton.IsCheckedProperty, true);
        }
    }
}

использование:

<RadioButton GroupName="grp1" Content="Value 1"
    helpers:RadioButtonHelper.RadioValue="val1" helpers:RadioButtonHelper.RadioBinding="{Binding SelectedValue}"/>
<RadioButton GroupName="grp1" Content="Value 2"
    helpers:RadioButtonHelper.RadioValue="val2" helpers:RadioButtonHelper.RadioBinding="{Binding SelectedValue}"/>
<RadioButton GroupName="grp1" Content="Value 3"
    helpers:RadioButtonHelper.RadioValue="val3" helpers:RadioButtonHelper.RadioBinding="{Binding SelectedValue}"/>
<RadioButton GroupName="grp1" Content="Value 4"
    helpers:RadioButtonHelper.RadioValue="val4" helpers:RadioButtonHelper.RadioBinding="{Binding SelectedValue}"/>