Simple WPF RadioButton Binding? Simple WPF RadioButton Binding? wpf wpf

Simple WPF RadioButton Binding?


I came up with a simple solution.

I have a model.cs class with:

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

I have Window1.xaml.cs file with DataContext set to model.cs. The xaml contains the 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" />

Here is my converter:

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

And of course, in Window1's resources:

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


I am very surprised nobody came up with this kind of solution to bind it against bool array. It might not be the cleanest, but it can be used very easily:

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

in 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}"/>

NOTE: you don't need two-way binding if you don't want to one checked by default. TwoWay binding is the biggest con of this solution.

Pros:

  • No need for code behind
  • No need for extra class (IValue Converter)
  • No need for extra enums
  • doesn't require bizarre binding
  • straightforward and easy to understand
  • doesn't violate MVVM (heh, at least I hope so)


Actually, using the converter like that breaks two-way binding, plus as I said above, you can't use that with enumerations either. The better way to do this is with a simple style against a ListBox, like this:

Note: Contrary to what DrWPF.com stated in their example, do not put the ContentPresenter inside the RadioButton or else if you add an item with content such as a button or something else, you will not be able to set focus or interact with it. This technique solves that. Also, you need to handle the graying of the text as well as removing of margins on labels or else it will not render correctly. This style handles both for you as well.

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

You now have the look and feel of radio buttons, but you can do two-way binding, and you can use an enumeration. Here's how...

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

Also, since we explicitly separated out the style that tragets the ListBoxItem rather than putting it inline, again as the other examples have shown, you can now create a new style off of it to customize things on a per-item basis such as spacing. (This will not work if you simply try to target ListBoxItem as the keyed style overrides generic control targets.)

Here's an example of putting a margin of 6 above and below each item. (Note how you have to explicitly apply the style via the ItemContainerStyle property and not simply targeting ListBoxItem in the ListBox's resource section for the reason stated above.)

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