Which Radio button in the group is checked?

Using WinForms; Is there a better way to find the checked RadioButton for a group? It seems to me that the code below should not be necessary. When you check a different RadioButton then it knows which one to uncheck… so it should know which is checked. How do I pull that information without doing a lot of if statements (or a switch).

     RadioButton rb = null;


if (m_RadioButton1.Checked == true)
{
rb = m_RadioButton1;
}
else if (m_RadioButton2.Checked == true)
{
rb = m_RadioButton2;
}
else if (m_RadioButton3.Checked == true)
{
rb = m_RadioButton3;
}
345694 次浏览

You can wire the CheckedEvents of all the buttons against one handler. There you can easily get the correct Checkbox.

// Wire all events into this.
private void AllCheckBoxes_CheckedChanged(Object sender, EventArgs e) {
// Check of the raiser of the event is a checked Checkbox.
// Of course we also need to to cast it first.
if (((RadioButton)sender).Checked) {
// This is the correct control.
RadioButton rb = (RadioButton)sender;
}
}

You could use LINQ:

var checkedButton = container.Controls.OfType<RadioButton>()
.FirstOrDefault(r => r.Checked);

Note that this requires that all of the radio buttons be directly in the same container (eg, Panel or Form), and that there is only one group in the container. If that is not the case, you could make List<RadioButton>s in your constructor for each group, then write list.FirstOrDefault(r => r.Checked).

You can use the CheckedChanged event for all your RadioButtons. Sender will be the unchecked and checked RadioButtons.

For those without LINQ:

RadioButton GetCheckedRadio(Control container)
{
foreach (var control in container.Controls)
{
RadioButton radio = control as RadioButton;


if (radio != null && radio.Checked)
{
return radio;
}
}


return null;
}

You can use an Extension method to iterate the RadioButton's Parent.Controls collection. This allows you to query other RadioButtons in the same scope. Using two extension methods, you can use the first determine whether any RadioButtons in the group are selected, then use the second to get the selection. The RadioButton Tag field can be used to hold an Enum to identify each RadioButton in the group:

    public static int GetRadioSelection(this RadioButton rb, int Default = -1) {
foreach(Control c in  rb.Parent.Controls) {
RadioButton r = c as RadioButton;
if(r != null && r.Checked) return Int32.Parse((string)r.Tag);
}
return Default;
}


public static bool IsRadioSelected(this RadioButton rb) {
foreach(Control c in  rb.Parent.Controls) {
RadioButton r = c as RadioButton;
if(r != null && r.Checked) return true;
}
return false;
}

Here's a typical use pattern:

if(!MyRadioButton.IsRadioSelected()) {
MessageBox.Show("No radio selected.");
return;
}
int selection = MyRadioButton.GetRadioSelection;

The OP wanted to get the checked RadioButton BY GROUP. While @SLaks' answer is excellent, it doesn't really answer the OP's main question. To improve on @SLaks' answer, just take the LINQ one step further.

Here's an example from my own working code. Per normal WPF, my RadioButtons are contained in a Grid (named "myGrid") with a bunch of other types of controls. I have two different RadioButton groups in the Grid.

To get the checked RadioButton from a particular group:

List<RadioButton> radioButtons = myGrid.Children.OfType<RadioButton>().ToList();
RadioButton rbTarget = radioButtons
.Where(r => r.GroupName == "GroupName" && r.IsChecked)
.Single();

If your code has the possibility of no RadioButtons being checked, then use SingleOrDefault() (If I'm not using tri-state buttons, then I always set one button "IsChecked" as a default selection.)

if you want to save the selection to file or any else and call it later, here what I do

string[] lines = new string[1];


lines[0]  = groupBoxTes.Controls.OfType<RadioButton>()
.FirstOrDefault(r => r.Checked).Text;


File.WriteAllLines("Config.ini", lines);

then call it with

string[] ini = File.ReadAllLines("Config.ini");
groupBoxTes.Controls.OfType<RadioButton>()
.FirstOrDefault(r => (r.Text == ini[0])).Checked = true;

If you want to get the index of the selected radio button inside a control you can use this method:

public static int getCheckedRadioButton(Control c)
{
int i;
try
{
Control.ControlCollection cc = c.Controls;
for (i = 0; i < cc.Count; i++)
{
RadioButton rb = cc[i] as RadioButton;
if (rb.Checked)
{
return i;
}
}
}
catch
{
i = -1;
}
return i;
}

Example use:

int index = getCheckedRadioButton(panel1);

The code isn't that well tested, but it seems the index order is from left to right and from top to bottom, as when reading a text. If no radio button is found, the method returns -1.

Update: It turned out my first attempt didn't work if there's no radio button inside the control. I added a try and catch block to fix that, and the method now seems to work.

In addition to the CheckedChangedEvent wiring one could use the Controls "Tag" property to distinguish between the radio buttons... an (spaghetti code) alternative would be the "TabIndex" property ;P

Sometimes in situations like this I miss my youth, when Access was my poison of choice, and I could give each radio button in a group its own value.

My hack in C# is to use the tag to set the value, and when I make a selection from the group, I read the value of the tag of the selected radiobutton. In this example, directionGroup is the group in which I have four five radio buttons with "None" and "NE", "SE", "NW" and "SW" as the tags on the other four radiobuttons.

I proactively used a button to capture the value of the checked button, because because assigning one event handler to all of the buttons' CHeckCHanged event causes EACH button to fire, because changing one changes them all. So the value of sender is always the first RadioButton in the group. Instead, I use this method when I need to find out which one is selected, with the values I want to retrieve in the Tag property of each RadioButton.

  private void ShowSelectedRadioButton()
{
List<RadioButton> buttons = new List<RadioButton>();
string selectedTag = "No selection";
foreach (Control c in directionGroup.Controls)
{
if (c.GetType() == typeof(RadioButton))
{
buttons.Add((RadioButton)c);
}
}
var selectedRb = (from rb in buttons where rb.Checked == true select rb).FirstOrDefault();
if (selectedRb!=null)
{
selectedTag = selectedRb.Tag.ToString();
}


FormattableString result = $"Selected Radio button tag ={selectedTag}";
MessageBox.Show(result.ToString());
}

FYI, I have tested and used this in my work.

Joey

The GroupBox has a Validated event for this purpose, if you are using WinForms.

private void grpBox_Validated(object sender, EventArgs e)
{
GroupBox g = sender as GroupBox;
var a = from RadioButton r in g.Controls
where r.Checked == true select r.Name;
strChecked = a.First();
}

For developers using VB.NET


Private Function GetCheckedRadio(container) As RadioButton
For Each control In container.Children
Dim radio As RadioButton = TryCast(control, RadioButton)


If radio IsNot Nothing AndAlso radio.IsChecked Then
Return radio
End If
Next


Return Nothing
End Function

Yet another try - Using event lambda expressions

As form initializes a single event handler can be assigned to all controls of type RadioButton in a group box, and use .tabIndex or .tag property to identify what option is checked when it changes.

This way you can subscribe to any of the events of each radio button in one go

int priceOption = 0;
foreach (RadioButton rbtn in grp_PriceOpt.Controls.OfType<RadioButton>())
{
rbtn.CheckedChanged += (o, e) =>
{
var button = (RadioButton)o;
if (button.Checked)
{
priceOption = button.TabIndex;
}
};
}

As events are assigned to radio buttons only, no type checking of sender is implemented.

Also notice as we loop all buttons this could be a perfect moment to assign data properties, change text etc.