Johnathan1
Johnathan1

Reputation: 2331

Simple WPF RadioButton Binding?

What is the simplest way to bind a group of 3 radiobuttons to a property of type int for values 1, 2, or 3?

Upvotes: 83

Views: 160053

Answers (11)

Ozgur Saklanmaz
Ozgur Saklanmaz

Reputation: 564

I improved the first accepted answer a bit by binding it to an enum to make it easier to use.

public enum ExportType
{
    Excel,
    PDF,
    CSV,
    HTML,
    TEXT
}

In this converter, the enum name connected to ConverterParameter must be written.

public class ExportTypeToBoolConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return Enum.TryParse(parameter?.ToString(), out ExportType type) && value?.Equals(type) == true;
    }

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

You can connect it to RadioButton as below.

<RadioButton GroupName="Export"
             Content="Text"
             IsChecked="{Binding Path=SelectExportType, Converter={StaticResource ExportTypeToBoolConverter}, ConverterParameter=TEXT}"/>

Upvotes: 0

Mark A. Donohoe
Mark A. Donohoe

Reputation: 30328

Improved and Now-Recommended Answer (e.g. approach 2.0)

While I provided the accepted answer to the question here on this page, as the commenters correctly call out, even though it's powerful and quite flexible, it's complexity is far from ideal when you just want to use simple RadioButton controls as-is.

As such, I've come up with a newer approach, shown below, that instead uses a custom IValueConverter and the power of Binding.DoNothing in it's ConvertBack method, the magic sauce that makes two-way bindings to RadioButton controls now work as one would expect.

The RadioButtonValueConverter

Let's take a look at the converter itself:

public class RadioButtonValueConverter : MarkupExtension, IValueConverter {

    public RadioButtonValueConverter(object optionValue)
        => OptionValue = optionValue;

    public object OptionValue { get; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        => value.Equals(OptionValue);

    public object ConvertBack(object isChecked, Type targetType, object parameter, CultureInfo culture)
        => (bool)isChecked        // Is this the checked RadioButton? If so...
            ? OptionValue         // Send 'OptionValue' back to update the associated binding. Otherwise...
            : Binding.DoNothing;  // Return Binding.DoNothing, telling the binding 'ignore this change'

    public override object ProvideValue(IServiceProvider serviceProvider)
        => this;
}

The magic sauce is in the use of Binding.DoNothing in the ConvertBack function. Since with RadioButton controls, there can only be one active option per 'group' (i.e. only one with IsChecked set to true), we ensure only the specific RadioButton with that value is the only one whose binding updates the source. Those on the other RadioButton instances simply do nothing.

Here's how you use it to bind to an int value as the OP asked (below, 'cv' is the imported namespace where the converter code resides, and the value you pass to the converter is the value that particular RadioButton represents)...

<RadioButton Content="One"   IsChecked="{Binding SomeIntProp, Converter={cv:RadioButtonValueConverter 1}}" />
<RadioButton Content="Two"   IsChecked="{Binding SomeIntProp, Converter={cv:RadioButtonValueConverter 2}}" />
<RadioButton Content="Three" IsChecked="{Binding SomeIntProp, Converter={cv:RadioButtonValueConverter 3}}" />

Simplifying further with a custom Binding Subclass

While the above works, that's a lot of repeated code and for 90% of the time, you aren't doing anything special with the binding or converter. As such, let's try to simplify things with a RadioButtonBinding that sets up the converter for you. Here's the code...

public class RadioButtonBinding : Binding {

    public RadioButtonBinding(string path, object optionValue)
    : base(path)
        => Converter = new RadioButtonValueConverter(optionValue);
}

With this new binding, the call site is greatly simplified (here, 'b' is the imported namespace where the binding code resides)...

<RadioButton Content="One"   IsChecked="{b:RadioButtonBinding SomeIntProp, 1}" />
<RadioButton Content="Two"   IsChecked="{b:RadioButtonBinding SomeIntProp, 2}" />
<RadioButton Content="Three" IsChecked="{b:RadioButtonBinding SomeIntProp, 3}" />

Note: Make sure you don't also set the Converter argument or you will defeat the entire point of using this!

Binding to Enum Values

The above example dealt with basic scalars (e.g. 1, 2, 3). However, what if the value we want to is an enumeration such as the following?

public enum TestEnum {
    yes,
    no,
    maybe,
    noIdea
}

The syntax is the same, but at the call-site, we need to be more specific about the value we're binding to making it much more verbose. (For instance, if you try and pass 'yes' by itself, it will be treated as a string, not an enum, so it will fail the equality check.)

Here's the converter version's call-site (here, 'v' is the imported namespace where the enum values reside)...

<RadioButton Content="Yes"     IsChecked="{Binding SomeEnumProp, Converter={cv:RadioButtonValueConverter {x:Static v:TestEnum.yes}}}" />
<RadioButton Content="No"      IsChecked="{Binding SomeEnumProp, Converter={cv:RadioButtonValueConverter {x:Static v:TestEnum.no}}}" />
<RadioButton Content="Maybe"   IsChecked="{Binding SomeEnumProp, Converter={cv:RadioButtonValueConverter {x:Static v:TestEnum.maybe}}}" />
<RadioButton Content="No Idea" IsChecked="{Binding SomeEnumProp, Converter={cv:RadioButtonValueConverter {x:Static v:TestEnum.noIdea}}}" />

And while simpler, here's the binding version's call-site, better, but still verbose...

<RadioButton Content="Yes"     IsChecked="{b:RadioButtonBinding SomeEnumProp, {x:Static v:TestEnum.yes}}" />
<RadioButton Content="No"      IsChecked="{b:RadioButtonBinding SomeEnumProp, {x:Static v:TestEnum.no}}" />
<RadioButton Content="Maybe"   IsChecked="{b:RadioButtonBinding SomeEnumProp, {x:Static v:TestEnum.maybe}}" />
<RadioButton Content="No Idea" IsChecked="{b:RadioButtonBinding SomeEnumProp, {x:Static v:TestEnum.noIdea}}" />

Enum-Type-Specific Variants

If you know you will be binding to a particular enum type on many occasions, you can simplify the above by subclassing the earlier RadioButtonValueConverter and RadioButtonBinding to be enum-specific variants.

Below is doing exactly that with TestEnum defined above, like so...

// TestEnum-specific Converter
public class TestEnumConverter : RadioButtonValueConverter {

    public TestEnumConverter(TestEnum optionValue)
    : base(optionValue) {}
}

// TestEnum-specific Binding
public class TestEnumBinding : RadioButtonBinding {

    public TestEnumBinding(string path, TestEnum value)
    : base(path, value) { }
}

And here are the call sites...

<!- Converter Variants -->
<RadioButton Content="Yes"     IsChecked="{Binding SomeTestEnumProp, Converter={cv:TestEnumConverter yes}}" />
<RadioButton Content="No"      IsChecked="{Binding SomeTestEnumProp, Converter={cv:TestEnumConverter no}}" />
<RadioButton Content="Maybe"   IsChecked="{Binding SomeTestEnumProp, Converter={cv:TestEnumConverter maybe}}" />
<RadioButton Content="No Idea" IsChecked="{Binding SomeTestEnumProp, Converter={cv:TestEnumConverter noIdea}}" />

<!- Binding Variants -->
<RadioButton Content="Yes"     IsChecked="{b:TestEnumBinding SomeTestEnumProp, yes}" />
<RadioButton Content="No"      IsChecked="{b:TestEnumBinding SomeTestEnumProp, no}" />
<RadioButton Content="Maybe"   IsChecked="{b:TestEnumBinding SomeTestEnumProp, maybe}" />
<RadioButton Content="No Idea" IsChecked="{b:TestEnumBinding SomeTestEnumProp, noIdea}" />

As you can see, the XAML parser automatically handles the string-to-enum conversion for you making your code much easier to read. Can't get much simpler than that! :)

Sidenote: One nice thing about the versions where you explicitly specify the enum value in its more-verbose declaration is you get auto-completion for the enum's cases. You don't get that with the enum-type-specific versions that convert the string for you. However, the latter will fail to compile if you use an invalid string value so the tradeoff is brevity vs auto-complete convenience.

Upvotes: 6

Mark A. Donohoe
Mark A. Donohoe

Reputation: 30328

Updated Answer with a Simpler Approach

Please see my new answer posted on this same page for a completely different, and much simpler approach to solving this issue. That new approach uses custom IValueConverter and Binding subclasses which let you go back to using a true RadioButton instead of a heavily-styled ListBox, as shown here.

Again, unless there are other benefits you'd personally gain when using a ListBox subclass, the other approach is what I now recommend.

Original Answer

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>

Upvotes: 42

Mani
Mani

Reputation: 21

Aviad P.s answer works very well. However I had to change the equality check to compare strings in OnRadioBindingChanged otherwise the enum was compared to the string value and no radio button was checked initially.

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

Upvotes: 1

wondra
wondra

Reputation: 3573

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)

Upvotes: 55

Ahmed Fwela
Ahmed Fwela

Reputation: 973

I created an attached property based on Aviad's Answer which doesn't require creating a new class

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

usage :

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

Upvotes: 1

ghord
ghord

Reputation: 13797

I've come up with solution using Binding.DoNothing returned from converter which doesn't break two-way binding.

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

Usage:

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

Radio button bindings:

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

Upvotes: 5

Mauro Sampietro
Mauro Sampietro

Reputation: 2814

Sometimes it is possible to solve it in the model like this: Suppose you have 3 boolean properties 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;
        }
    }
}

You get the idea. Not the cleanest thing, but easy.

Upvotes: 2

Martin
Martin

Reputation: 5623

This example might be seem a bit lengthy, but its intention should be quite clear.

It uses 3 Boolean properties in the ViewModel called, FlagForValue1, FlagForValue2 and FlagForValue3. Each of these 3 properties is backed by a single private field called _intValue.

The 3 Radio buttons of the view (xaml) are each bound to its corresponding Flag property in the view model. This means the radio button displaying "Value 1" is bound to the FlagForValue1 bool property in the view model and the other two accordingly.

When setting one of the properties in the view model (e.g. FlagForValue1), its important to also raise property changed events for the other two properties (e.g. FlagForValue2, and FlagForValue3) so the UI (WPF INotifyPropertyChanged infrastructure) can selected / deselect each radio button correctly.

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

The xaml looks like this:

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

Upvotes: 2

Aviad P.
Aviad P.

Reputation: 32629

I know it's way way overdue, but I have an alternative solution, which is lighter and simpler. Derive a class from System.Windows.Controls.RadioButton and declare two dependency properties RadioValue and RadioBinding. Then in the class code, override OnChecked and set the RadioBinding property value to that of the RadioValue property value. In the other direction, trap changes to the RadioBinding property using a callback, and if the new value is equal to the value of the RadioValue property, set its IsChecked property to true.

Here's the code:

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

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

Hope someone finds this useful after all this time :)

Upvotes: 23

Johnathan1
Johnathan1

Reputation: 2331

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>

Upvotes: 103

Related Questions