Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

Reusable WPF RadioButton Control (Data Binding to enum)

4.00/5 (4 votes)
28 Jul 2014CPOL3 min read 21.8K   185  
Reusable WPF RadioButton control which takes the pain away

Introduction

I needed to add a simple 3 choice selection to my WPF project, using RadioButton control seemed an obvious choice, but the amount of work for such a simple task was ridiculous. So I ended up creating a reusable control with a pretty simple usage.

Background

The idea of the control is to simply pass an array of Enums (or strings) and it will create the radio button for each one. Then, just handle OnSelectionChanged event.

Here are the screenshots from the attached example application. First group of three RadioButtons are added as Enums with text labels. The second group of three RadioButtons are added as array of three strings.

Fig 1. For Group 1 Selected item is of type MyEnum, for Group 2 it's a String.

Using the Code

From XAML point of view, the usage is very simple:

XML
<my:RadioButtonCtrl x:Name="radioButtonCtrl" Margin="2,2,2,2" />

In code behind, add list of items, button labels (Content) will be Enum names converted to String:

C#
radioButtonCtrl.Add(MyEnum.Enum1, MyEnum.Enum2, MyEnum.Enum3);

Alternatively, you can add strings instead of Enum:

C#
radioButtonCtrl.Add("Item1", "Item2", "Item3");

Another option is to add an array of String/Enum pairs, in this case the String is used as a button label (Content) and the Enum will be passed to OnSelectionChanged event:

C#
radioButtonCtrl.Add(
                new RadioButtonCtrl.MyPair("Item1", MyEnum.Enum1),
                new RadioButtonCtrl.MyPair("Item2", MyEnum.Enum2),
                new RadioButtonCtrl.MyPair("Item3", MyEnum.Enum3)
                );

To programmatically set the selected button, use Selected property. You can pass the button index, label or the Enum tag, all have the same result:

C#
radioButtonCtrl.Selected = 0;
radioButtonCtrl.Selected = "Item1";
radioButtonCtrl.Selected = MyEnum.Enum1;

To handle the selection, add OnSelectionChanged event handler:

C#
radioButtonCtrl.OnSelectionChanged += 
new RadioButtonCtrl.SelectedEventHandler(radioButtonCtrl_OnSelectionChanged);

void radioButtonCtrl_OnSelectionChanged(object sender, RadioButtonCtrl.SelectedEventArgs e)
{
    label1.Content = String.Format("Selected: {0} ({1}), Group: '{2}'",
            e.NewSelection, e.NewSelection.GetType().Name, e.Group);
}

Please note that if you add more than one RadioButtonControl to your window, it's recommended that for each control you should set a unique Group name, otherwise Microsoft becomes confused. Also, currently Group name should be set before calling Add() function.

C#
radioButtonCtrl2.Group = "Group 2";
radioButtonCtrl2.Add("Item1", "Item2", "Item3");

This way, you can also use single OnSelectionChanged even handler for multiple groups (if you want), the group name will be passed in the RadioButtonCtrl.SelectedEventArgs, see the attached example code.

Layout Options

To change the Horizontal alignment of buttons, use FlowDirection property, Fig 2 demonstrates that the Group2 FlowDirection is set to RightToLeft:

C#
radioButtonCtrl2.FlowDirection = FlowDirection.RightToLeft;
Fig 2. Group 2 FlowDirection is set to "FlowDirection.RightToLeft".

For the Vertical button alignment, there are also two options. If control Height is not set (or set to NaN) - the radio buttons will be spread evenly vertically when the parent window is resized.

But if the control Height is set explicitly in XAML or code behind, the buttons vertical positions won't change when re-sizing the parent.

C#
<my:RadioButtonCtrl Grid.Row="0" Height="80"  x:Name="radioButtonCtrl1" />
<my:RadioButtonCtrl Grid.Row="1" Height="NaN" x:Name="radioButtonCtrl2" />
Fig 3. Group 1 Height is set explicitly and doesn't change when the window is resized.

Points of Interest

If you are just after a quick solution for the RadioButton problem - download the RadioButtonControl.dll and include it in your project. Usage should be pretty straight forward.

Otherwise, if you want to know how it works, please download the source code and have a look.

The only point of interest which is worth mentioning here is the RadioButton.IsChecked property DataBinding implementation. Since the RadioButtons are added to the control from the code, not XAML, the DataBinding should be created in the code as well.

To simplify DataBinding creation, I added a simple MyBinding class. The interesting part here is the IValueConverter implementation to link the buttons IsChecked property to the RadioButtonCtrl.Selected property.

C#
public void Add(string label, Enum tag = null)
{
    RadioButton btn = new RadioButton();

    ...

    if (tag == null)
    {
        btn.Tag = label;
    }
    else
    {
        btn.Tag = tag;
    }

    MyBinding binding1 = new MyBinding("Selected", this, new EnumMatchToBooleanConverter(), btn.Tag);
    btn.SetBinding(RadioButton.IsCheckedProperty, binding1);

    ...

    stackPanel.Children.Add(btn);
}

The converter implementation is based on Christian Moser article. I slightly modified the converter implementation to support both RadioButtons usage scenarios - using just the String label or using the Enum Tag.

History

I'm planning to change OnSelectionChanged event handler to WPF-ish style using Dependency Property. Currently, it's a quick piece of work, but it works and works well.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)