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 Enum
s (or string
s) 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 RadioButton
s are added as Enum
s with text labels. The second group of three RadioButton
s are added as array of three string
s.
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:
<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
:
radioButtonCtrl.Add(MyEnum.Enum1, MyEnum.Enum2, MyEnum.Enum3);
Alternatively, you can add string
s instead of Enum
:
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:
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:
radioButtonCtrl.Selected = 0;
radioButtonCtrl.Selected = "Item1";
radioButtonCtrl.Selected = MyEnum.Enum1;
To handle the selection, add OnSelectionChanged
event handler:
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.
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 :
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.
<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 RadioButton
s 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.
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 RadioButton
s 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.