简单的WPF RadioButton绑定?

Joh*_*an1 51 wpf binding radio-button

对于值1,2或3,将一组3个radiobuttons绑定到int类型属性的最简单方法是什么?

Joh*_*an1 85

我提出了一个简单的解决方案.

我有一个model.cs类:

private int _isSuccess;
public int IsSuccess { get { return _isSuccess; } set { _isSuccess = value; } }
Run Code Online (Sandbox Code Playgroud)

我有Window1.xaml.cs文件,DataContext设置为model.cs.xaml包含radiobuttons:

<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" />
Run Code Online (Sandbox Code Playgroud)

这是我的转换器:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,在Window1的资源中:

<Window.Resources>
    <local:RadioBoolToIntConverter x:Key="radioBoolToIntConverter" />
</Window.Resources>
Run Code Online (Sandbox Code Playgroud)

  • 比接受的答案更清洁 - 正是我所寻找的. (7认同)
  • 但是,请相信,不"if(true)return true;否则返回false;" 结构体... :-) (7认同)
  • @MarqueIV这个问题很容易解决,可以在第一个评论中看到这个问题的接受答案:http://stackoverflow.com/questions/397556/how-to-bind-radiobuttons-to-an-enum (3认同)
  • 您只需要更改转换器以解析枚举即可 (2认同)
  • @ user1151923 ...有一件事,在这种情况下返回'Binding.DoNothing'而不是'DependencyProperty.UnsetValue'是不正确的吗? (2认同)
  • 如何设置默认值.那是在首次显示窗口时选择第一个单选按钮? (2认同)

Mar*_*eIV 33

实际上,使用像这样的转换器打破了双向绑定,加上我上面所说的,你也不能使用枚举.更好的方法是使用针对ListBox的简单样式,如下所示:

注:相反的是DrWPF.com在他们的例子说明,也没有把里面单选按钮,否则如果添加与内容的项目,如按钮或别的东西,你将无法设定对焦或与其交互的ContentPresenter .这种技术解决了这个问题.此外,您需要处理文本的灰色以及删除标签上的边距,否则它将无法正确呈现.这种风格也适合你.

<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>
Run Code Online (Sandbox Code Playgroud)

您现在拥有单选按钮的外观,但您可以进行双向绑定,并且可以使用枚举.这是如何做...

<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>
Run Code Online (Sandbox Code Playgroud)

此外,由于我们明确地将传承ListBoxItem的样式分离出来而不是将其置于内联中,再次如其他示例所示,您现在可以创建一个新样式来自定义基于每个项目的内容,例如间距.(如果您只是尝试将ListBoxItem作为键控样式来覆盖通用控件目标,则此方法无效.)

这是一个在每个项目上下放置6的边距的示例.(注意你必须通过ItemContainerStyle属性显式应用样式,而不是简单地在ListBox的资源部分中定位ListBoxItem,原因如上所述.)

<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>
Run Code Online (Sandbox Code Playgroud)

  • +1,但如果这是将一组3个radiobuttons绑定到int类型为1,2或3的类型的属性的最简单方法,那对WPF来说真的不是很好.这真的超出了我为什么他们让最简单的事情变得如此困难和不必要的复杂 (46认同)
  • 天哪,简单?尽管你的答案和努力都得到了应有的尊重,但称这种"简约风格"是相当遥远的,不是吗?WPF的核心思想和感知优势不是Winforms更好的数据绑定吗?在多年的WPF进入市场之后,简单的单选按钮(和单选按钮分组)如何破坏绑定是我无法理解的.对MS的耻辱. (3认同)
  • 这实际上只是做了一点,因为它设置了一个列表框,这是一个单一的控件,这就是为什么绑定工作如此简单(一旦你通过疯狂的设置它.)但是,我同意RadioButton类肯定有一些约束力的缺点.当然,你可以通过代码隐藏更简单地做到这一点,但是这样,你只需要做一次这样的样式,然后再使用它就容易多了. (2认同)

won*_*dra 28

我很惊讶没有人想出这种解决方案将它绑定到bool数组.它可能不是最干净的,但它可以很容易地使用:

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

在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}"/>
Run Code Online (Sandbox Code Playgroud)

注意:如果您不想默认选中一个,则不需要双向绑定.TwoWay绑定是此解决方案的最大缺点.

优点:

  • 不需要代码
  • 无需额外课程(IValue转换器)
  • 无需额外的枚举
  • 不需要bizzare绑定
  • 简单易懂
  • 没有违反MVVM(嘿,至少我希望如此)

  • 只是一个小建议:从C#6开始,你将不再需要一个支持领域.只需使用AutoProperty:public bool [] ModeArray {get; } = new bool [] {true,false,false}; (3认同)
  • 这怎么可能是数据驱动的?如果我从数据库加载值,如何以编程方式设置选项? (2认同)

Avi*_* P. 17

我知道这是过时的方式,但我有一个替代解决方案,它更轻,更简单.从中派生一个类System.Windows.Controls.RadioButton并声明两个依赖属性RadioValueRadioBinding.然后在类代码中,覆盖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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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}"/>
Run Code Online (Sandbox Code Playgroud)

希望有人在这段时间后发现这个有用:)


gho*_*ord 5

我想出了使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

<converters:EnumToCheckedConverter x:Key="SourceConverter" Type="{x:Type monitor:VariableValueSource}" />
Run Code Online (Sandbox Code Playgroud)

单选按钮绑定:

<RadioButton GroupName="ValueSource" 
             IsChecked="{Binding Source, Converter={StaticResource SourceConverter}, ConverterParameter=Function}">Function</RadioButton>
Run Code Online (Sandbox Code Playgroud)