If this is your first visit, be sure to
check out the FAQ by clicking the
link above. You may have to register or Login
before you can post: click the register link above to proceed. To start viewing messages,
select the forum that you want to visit from the selection below.

A couple of radio button questions

I have two questions regarding radio buttons. I'm using Visual Studio 2008 Express with the targeted .Net framework being 2.0. I'm also still very new to C# and just trying to learn as I go.

My first question is that I am using CheckChanged, but I noticed that by doing that the code for not just the radio button that is checked is ran, but the code for the one that gets unchecked will run as well.

How do I stop that from happening?

My second question is about saving which radio buttons are checked. I have no idea about this. I want the radio buttons that the user selects to be saved so that the next time the program is opened the user doesn't have to go and reselect everything again.

Can I use Settings.settings? If so, how? Or do I need to use something else? I've searched for this, but nothing I've come across makes sense to me.

Re: A couple of radio button questions

Originally Posted by Vorkosigan

My first question is that I am using CheckChanged, but I noticed that by doing that the code for not just the radio button that is checked is ran, but the code for the one that gets unchecked will run as well.

How do I stop that from happening?

You don't. From within the CheckChanged handlers, use the Checked property to determine the state. If you only want to handle the condition where an item is checked, then check for if( Checked == true ) inside the handler.

Re: A couple of radio button questions

Thanks Arjay, that did the trick. I knew it had to be something simple, but just wasn't sure what.

Another radio button question that I've been trying to figure out... I have an exception that catches when certain radio buttons on selected at the same time. It's doing exactly what i want it to do except for one thing... when it catches the exception, it still selects the radio button that caused it to be thrown.

Is there way to keep it from being selected when the exception is thrown or to change it back to unselected when the exception is caught?

Re: A couple of radio button questions

Originally Posted by Vorkosigan

I have an exception that catches when certain radio buttons on selected at the same time. It's doing exactly what i want it to do except for one thing... when it catches the exception, it still selects the radio button that caused it to be thrown.

Let me start out by saying that the selection state of radio buttons within a group is mutually exclusive - meaning only one radio button within the group can be selected at a time.

I mention this because often developers that don't understand UI design misuse a radio button and allow more than one radio button selected within the group. If more that one item can be selected in a group then a more appropriate control to use is a checkbox.

Given the idea that radio button selection is exclusive within a group, the AutoCheck property can be used to help promote exclusivity within a group of buttons.

Allow me to define what a group is. In Win32 programming, a group of radio buttons was a series of radios in consecutive tab order with the first button's group style set to true. So let's say that there are 5 consecutive radio buttons (consecutive by tab order) and the 1st and 4th radio buttons had their group property set to true. We would end up with two groups of radio buttons (G1 == 1,2,3; G2 == 4,5). If only the 1st radio button had the group set to true, then all 5 radio buttons would be in the group.

In .Net Radio buttons are grouped differently. They are grouped based on the control that contains them. Unfortunately, there isn't a 'Group' property to control whether a series of radio buttons can be broken into separate groups.

In .Net, groups are controlled by the parent control. If 3 radio buttons are children of the form, then they will all be part of the same group. If you have 5 radio buttons and you want to have two groups, then you'll need to make some of the radio buttons children of another control (within the form). GroupBox and Panel controls make excellent control containers to group radio buttons.

So to review, if you set the AutoCheck property to true for each radio button within a 'group', then the framework will automatically check the selected button (and uncheck any previous radio button for you).

So now that we have the 'visual' part taken care of. Let's discuss how to determine which radio button within a 'group' has been selected. To do this, we borrow the concept from MFC and it's DDX mechanism. In MFC, radio buttons with a consecutive tab order and the 1st button has the group style set to true can use the DDX variable mechanism to wire up an integer that gets set to the zero-based index of the selected radio button within the group (or -1 if no radio buttons are selected).

This is pretty cool because, you don't need to enumerate each button and check if it's been selected. All you need to do is check one integer variable.

Unfortunately, .Net doesn't have this feature builtin. The good news is that it isn't hard to implement. It just takes two methods: One to retrieve the index of the selected radio button within the group and another method to clear the selected state within the group.

All you need to do is pass in the parent control that contains the radio buttons. Remember the parent control is what defines radio buttons within a group. Radio buttons with different control parents are considered to be in different groups.

See the attached project for a sample that contains 3 radio buttons 'grouped' within a GroupBox control and 2 radio buttons 'Grouped' by the form. The sample also contains buttons to clear the selections by group and when the OK button is hit, the selected values of the radio button groups are displayed.

Re: A couple of radio button questions

To follow up, I find the approach outlined above to be easier that trying to manually track the states using the CheckChanged events. Of course, not always will the above approach be sufficient (but that isn't to say that you can combine both approaches).

Btw, here is the code to retrieve the selected radio buttons when the OK button is pressed.

Re: A couple of radio button questions

I'm gonna take another look at this tomorrow when I am more awake so that I can fully understand what you are trying to tell me, but so far it looks like it's more complex than I was thinking it should be.

I'll try to describe what I am doing a little better. I have 5 panels as my containers. 4 of those panels have 5 radio buttons. The other one has 6 radio buttons. One of the buttons in each panel is set as a "default" to have no value. Each panel has it's own variables that get assigned depending on the radio button that is selected. That part works just fine.

Where the problem comes in is a little hard to explain. There's another variable that needs to be taken into consideration that can only come from adding up a number that is based on which radio buttons are selected across all of those panels.

So for this other variable, the first four radio buttons in the first 4 panels are valued at 2, 3, 4 and 5. The first 5 radio buttons of the last panel are valued at 2, 4, 6, 8 and 9. These are separate values from the variables that get assigned when the radio buttons are checked.

The total of these secondary values can only add up to 9. This is where the problem is. If some checks the 5th radio button of the 5th panel, they are already at 9. So I have an exception that is thrown when they then try to select another radio button that makes that total go over that number. That pops up a message stating that the total can't go over 9, but it still lets the radio button get checked.

I'm sure it seems pretty awkward, but I hope that's a clearer picture of what I am trying to do and why.

Re: A couple of radio button questions

I am still not seeing how that will work with what I've got. This is the method for the secondary values that I was talking about that is ran for each CheckedChanged event.

Code:

removed code

Isn't there a way to simply uncheck the last radio button that was selected, or what might be easier is to select the the None radio button for the particular Panel that had the last selected radio button that caused the exception? Or maybe I am just not seeing how to make what you've shown me fit with what I have?

Re: A couple of radio button questions

You may wonder why I've approached the problem with a few radio button helper methods that operate on radio buttons within a group:
ClearRadioButtonsInGroup, GetSelectedRadioIndex, SetRadioButtonByIndex (which I just added in the updated source).

The reason for this is to follow these design rules:
1) Separate the UI logic from the state
2) Leverage the UI to help guide the user into performing the correct actions.

The first rule allows me to use "divide and conquer" approach. I only need to worry about one aspect of the problem (i.e. the UI, windowy part) rather than tackling the complete problem. The 'windowy' part refers to any hide/show, enable/disable operations I might want to do on the radio buttons.

The state is the result of the UI selection and is different from the UI control state. You had mentioned earlier that the approach seemed complicated. What is going on here is a bit of Model/View design pattern (where the UI is separated from the state). In this simple example, the benefits aren't immediately obvious, but consider what would happen if you needed to pass the two states (Color and Size) to another form.

Would you rather pass around two enums of Color and Size to the form, or would you want to have the second form walk through the controls of the 1st form and try to determine the state based on the selected radio buttons in the first form?

Hopefully, you didn't choose the second approach, but if you did, consider what would happen if you decided to change how the Colors are displayed to the user in the first form. Instead of using radio buttons, say a combobox was used. If you took the second approach, you would need to also change the logic in the second form because it could no longer determine the color state by looking at the color radio buttons (because their aren't any color radio buttons).

The bottom line is to keep dependencies and interactions between forms to a minimum. By passing state around, you are free to change the UI in one place without effecting other aspects of the system.

The second rule means to be proactive with the UI and prevent the user from doing something that they shouldn't be doing. In our case, it means to disable size radio buttons that aren't valid based on the Color selection. This is very different from allowing a user to make any selection and then telling the user afterword that their selection was invalid. A good UI should be proactive and guide the user through visual cues on what options are valid.

As I mentioned, I modified the Radio.zip program attached above to handle the new dependent state functionality. I added a new
SetRadioButtonByIndex helper method. This is consistent with the "separate the UI from state" rule because when I want to set the selected state of say the Size enum, I don't want to have to walk through a bunch of radio buttons (sure, this method does it, but it keeps me from having to do it everytime I need to programmatically set radio button state).

Besides the new helper method. All I needed to do to set up the radio button Color dependencies is wire up the CheckedChanged event on each of the Color radio buttons. I wire them up to the same event handler.

Tip: It's usually a good idea to wire up events in similar controls to a single event handler if you can get away with it. This makes the code simpler if there is only one event handler (rather than a handler for each control).

You'll see the handler calls two other methods. Pretty simple. Let's look at the two methods.

Keeping with the design rules, we first call the EnableSizeRadioButtons method which enables or disables the size radio buttons based on the color selection.

Notice how the method doesn't walk through the color radio buttons to determine which one was selected? Instead it uses the helper method for getting the state. This allows this method to be very compact and easy to debug and maintain. This fulfills the 'guiding the user' rule because the user is only able to choose from enabled radio buttons.

The second ForceSizeRadioButtonSelection method in the CheckedChanged handler forces the Size state based on the Color state.

The functionality here between two methods is so simple in this case it could have been rolled in to one method. However, it usually is a good idea to keep the two separate it makes things easier to maintain if the business logic changes.

You'll notice that with a couple of small addional methods, I was able to add the Size dependent upon selected Color functionality. The new functionality also didn't require any changes to the existing clear functionality (nor did it break this functionality). The idea of view/model and these rules may appear complicated at first, but following these allows the developer to build up complex UI/state interactions in a maintainable manner.

Re: A couple of radio button questions

Just a thought: If the selected radio button of a group needs to be tracked, why not to just add a member variable for each group (or create a simple RadioButtonGroup class with such a field/property) that would always reference to it, and that would be updated inside the CheckChanged event handler?

Something like:

Code:

private RadioButton current = null; // This can also be set to initially point to the default option...
// ...
private void radioButton_CheckedChanged(Object sender, EventArgs e)
{
RadioButton rBtn = (RadioButton)sender;
if (rBtn.Checked)
current = rBtn;
// ...
}

(All the radio buttons of a group should use the same handler method in this case.)

For additional details, one could use Text and/or Tag properties of RadioButton class.

Last edited by TheGreatCthulhu; March 3rd, 2010 at 03:13 PM.
Reason: Forgot to add if (rBtn.Checked) part...

Re: A couple of radio button questions

Originally Posted by TheGreatCthulhu

Just a thought: If the selected radio button of a group needs to be tracked, why not to just add a member variable for each group (or create a simple RadioButtonGroup class with such a field/property) that would always reference to it, and that would be updated inside the CheckChanged event handler?

Something like:

Code:

private RadioButton current = null; // This can also be set to initially point to the default option...
// ...
private void radioButton_CheckedChanged(Object sender, EventArgs e)
{
RadioButton rBtn = (RadioButton)sender;
if (rBtn.Checked)
current = rBtn;
// ...
}

(All the radio buttons of a group should use the same handler method in this case.)

For additional details, one could use Text and/or Tag properties of RadioButton class.

That sounds pretty good until you need to track several groups of radio buttons that have dependencies and conditions - then the whole thing becomes unwieldy pretty fast.

IMO, programming using Text and/or Tag is a more like an asp scripting hack.

Re: A couple of radio button questions

Originally Posted by Arjay

IMO, programming using Text and/or Tag is a more like an asp scripting hack.

Well, OK, I agree about the Text property, but only partially about the Tag.
Sure, Tag can be misused, but I think it's OK to use it for tag-like data (integer index, or string description, or enum), or helper-data, or convenient additional data. I'm not saying that it should be used often, but when used as intended, I don't see why it would be considered a hack.

I, of course, respect your opinion, and your experience, but I still don't see how the interdependency could be so complex that a class based approach couldn't handle it (with a class to represent a radio button group).

I combined my idea with what you suggested, and the attached zip is what I came up with. I added some Size-option related behavior to spice it up a bit (one of the labels on the right provides a fictional remark for certain color/size combinations).

The rough design is shown in the attached image.
The RadioButtonGroup class tracks the current selection (if any), and provides a way to get the index of the selected radio button. The buttons are sorted by tab index (I chose to use SortedList<> instead of SortedDictionary<>).
There is an event fired whenever the state of the group is changed, and handlers are provided with all means necessary to do the app logic.

There is a potential problem with the AutoSelect property of the RadioButton class, since the RadioButtonGroup class relays on the assumption that clients won't change it, but it can be prevented one way or another, I didn't have time to deal with it.

The dialog has properties which enable the client to check the choices that were made after the dialog is closed.

This probably needs more work, but it's only to demonstrate the concept.
And, by the way, the Tag property is not used

Originally Posted by Arjay

That sounds pretty good until you need to track several groups of radio buttons that have dependencies and conditions - then the whole thing becomes unwieldy pretty fast.

I would appreciate if you could show me a scenario where this approach wouldn't work well.

P.S. Sorry if the code is not documented/commented enough; I suppose that, given this discussion, it's more or less self-explanatory.

Re: A couple of radio button questions

Thanks.
I suppose it could prove to be useful.
Iíve been considering the concept in my spare time, doing some experimentation; I think that this type of control shouldnít limit the usersí freedom to create their own arrangement of child controls (radio buttons), by forcing a certain layout or something alike.
Iíve been trying to find a good approach (at least as far as the design is considered), and what I feel is right is that this should be some sort of a Panel-like container control - a UserControl specialized for radio buttons and related tasks. The user would be able to use the IDE to drag & drop any child control, and any RadioButton control would receive a special treatment.

Iíve somewhat altered the RadioButtonGroup class from my previous post to suit this new problem, and created a UserControl-derived RadioButtonContainer on top of that.
As Iíve said, the AutoCheck property might introduce some problems, so in this implementation I chose to ignore it completely, without ever changing it.

However, the ability to dynamically add/remove radio buttons generates a new set of problems, as does the designer feature. It is mainly related to the fact that the radio buttons are sorted by tab index, and that the user can alter any of the indices after the buttons have been added to the RadioButtonContainer (at run-time, or at design-time...).
So, I added the TabOrderChanged event, and the Sort() method.

I overrided the OnLoad() method of RadioButtonContainer to ensure that if the user changed the tab order in the designer, the buttons will be reordered (by calling Sort()). But, now I think this was a wrong decision, since it makes the control harder to use and somewhat unpredictable (I had some problems with an instance of this class that is a child to a inactive TabPage control Ė the OnLoad() is not called until this tab page is shown Ė so one canít rely on it). This is probably a task that should be completed at the design time by the custom designer.

Anyway, the RadioButtonGroup does all or most of the work, and other types of controls may be created to reuse this functionality.
In the test project, thereís a bunch of radio buttons inside 4 RadioButtonContainer controls. The radio buttons represent options for the creation of a fantasy game caracter. The last two of the radio buttons in the Magic group box both have AutoCheck set to false, to demonstrate that this property is not used. This one, and other RadioButtonContainer controls show how they can conveniently be nested into other standard controls using docking. The ones inside the TabControl show that a RadioButtonContainer may contain other controls without any problems, and that it can be used with other child controls at the same level in the hierarchy.

I was just playing with the idea Ė so this needs a significant amount of testing and refinement. For example, I didn't consider thread safety issues. Documentation could be improved also. The attached class diagram shows all the significant methods, though.

P.S. To anyone who might find this code useful: you are free to use and modify it.