A couple of radio button questions
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 15 of 15

Thread: A couple of radio button questions

  1. #1
    Join Date
    Mar 2009
    Posts
    15

    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.

  2. #2
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,473

    Re: A couple of radio button questions

    Quote Originally Posted by Vorkosigan View Post
    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.

  3. #3
    Join Date
    Mar 2009
    Posts
    15

    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?

  4. #4
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,473

    Re: A couple of radio button questions

    Quote Originally Posted by Vorkosigan View Post
    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.

    Code:
    #region  Radio  Button  Helper  methods
     
    privatevoid   ClearRadioButtonsInGroup( Control parent )
    {
      foreach ( Control child in parent.Controls )
      {
        RadioButton radio = child asRadioButton;
     
        if ( radio != null )
        {
          radio.Checked = false;
        }
      }
    }
     
    ///<summary>
    /// Returns a zero-based index of the selected radio
    /// button within the group.
    ///</summary>
    ///<param name="parent">Control which contains the radio buttons</param>
    ///<returns>Index of selected radio button; -1 if none are selected</returns>
    privateint GetSelectedRadioIndex( Control parent )
    {
      int index = NoneSelectedIndex;
     
      var radioButtons = newSortedDictionary< int, RadioButton >( );
     
      // Add the radio buttons to the dictionary
      // sorted by tab index
      foreach ( Control child in parent.Controls )
      {
        RadioButton radio = child asRadioButton;
     
        if ( radio != null )
        {
          radioButtons.Add( radio.TabIndex, radio );
        }
      }
     
      // Walk through the dictionary (which are 
      // sorted by tab index) to retrieve the index
      // of the selected radio button.
      // NOTE: The first index within this group
      // will always be 0 regardless of the starting
      // tab index. This is done so you know that
      // 0 always represent the first radio button
      // within the group
      foreach ( int tabIndex in radioButtons.Keys )
      {
        index++;
     
        if ( radioButtons [ tabIndex ].Checked )
        {
          return index;
        }
      }
     
      return NoneSelectedIndex;
    }
     
    #endregion Radio Button Helper methods
     
    privatereadonlyint NoneSelectedIndex = -1;
    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.
    Attached Images Attached Images   
    Attached Files Attached Files
    Last edited by Arjay; March 3rd, 2010 at 12:21 PM.

  5. #5
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,473

    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.
    Code:
    private  void   btnOK_Click( object sender, EventArgs e )
    {
      Colors color = (Colors) GetSelectedRadioIndex( this.groupBox1 );
      Sizes size = (Sizes) GetSelectedRadioIndex( this );
     
      MessageBox.Show( String.Format( "Selected Color: {0}\nSelected Size: {1}", color, size ) );
    }


  6. #6
    Join Date
    Mar 2009
    Posts
    15

    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.

  7. #7
    Join Date
    Mar 2009
    Posts
    15

    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?
    Last edited by Vorkosigan; March 29th, 2010 at 02:48 PM.

  8. #8
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,473

    Re: A couple of radio button questions

    I've modified the source attached previously to create a dependency between the color radio buttons and the size radio buttons. Here are the rules:

    // Button dependency rules:
    // Red - all sizes are available
    // Green - large and medium is available
    // Blue - small is available



    The screenshot shows the Blue button selected which, according to the dependency rules, only the Small radio button is valid.

    Btw, to make the dependencies a bit more interesting, I've added a Medium radio button.
    Attached Images Attached Images  
    Last edited by Arjay; March 3rd, 2010 at 11:03 AM.

  9. #9
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,473

    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.

    Code:
    privatevoid  OnColorCheckedChanged( object sender, EventArgs e )
    {
      EnableSizeRadioButtons( );
      ForceSizeRadioButtonSelection( );
    }
    


    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.

    Code:
    ///<summary>
    /// Enables the size radio buttons based on the
    /// Color radio button selection
    ///</summary>
    privatevoid EnableSizeRadioButtons( )
    {
      // Enable all Size radio buttons
      this.radioButtonLarge.Enabled = true;
      this.radioButtonMedium.Enabled = true;
      this.radioButtonSmall.Enabled = true;
    
      // Button dependency rules:
      // Red - all sizes are available
      // Green - large and medium is available
      // Blue - small is available
      switch( (Colors) GetSelectedRadioIndex( groupBox1 ) )
      {
      caseColors.Green:
        this.radioButtonSmall.Enabled = false;
        break;
      caseColors.Blue:
        this.radioButtonLarge.Enabled = false;
        this.radioButtonMedium.Enabled = false;
        break;
      }
    }
    


    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.

    Code:
     
    ///<summary>
    /// Forces a size radio button based on selected color
    /// When Color.None or Color.Red is selected, does nothing
    /// When Color.Green is selected, clears Sizes.Small if selected
    /// When Color.Blue is selected, selects Sizes.Small
    ///</summary>
    privatevoid ForceSizeRadioButtonSelection( )
    {
      switch ( ( Colors ) GetSelectedRadioIndex( groupBox1 ) )
      {
      caseColors.Green:
        // Clear previous selection, if user had the
        // now unavailable Small option selected
        if ( Sizes.Small == ( Sizes ) GetSelectedRadioIndex( this ) )
        {
          SetRadioButtonByIndex( this, NoneSelectedIndex );
        }
        break;
     
      caseColors.Blue:
        // Since small is the only option, select it
        SetRadioButtonByIndex( this, ( int ) Sizes.Small );
        break;
      }
    }


    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.
    Last edited by Arjay; March 3rd, 2010 at 12:28 PM.

  10. #10
    Join Date
    Jan 2010
    Posts
    1,102

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

  11. #11
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,473

    Re: A couple of radio button questions

    Quote Originally Posted by TheGreatCthulhu View Post
    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.

  12. #12
    Join Date
    Jan 2010
    Posts
    1,102

    Re: A couple of radio button questions

    Quote Originally Posted by Arjay View Post
    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

    Quote Originally Posted by Arjay View Post
    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.
    Attached Images Attached Images  
    Attached Files Attached Files
    Last edited by TheGreatCthulhu; March 4th, 2010 at 12:40 PM. Reason: The P.S., and a thing or 2 to add.

  13. #13
    Arjay's Avatar
    Arjay is offline Moderator / MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    11,473

    Re: A couple of radio button questions

    TheGreatCthulhu, nice job on the alternate approach.

    Expanding on both approaches, I wonder if it wouldn't be helpful to create a user control that encapsulates the functionality?

  14. #14
    Join Date
    Jan 2010
    Posts
    1,102

    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.
    Attached Images Attached Images  
    Attached Files Attached Files

  15. #15
    Join Date
    Mar 2009
    Posts
    15

    Re: A couple of radio button questions

    Thanks guys. I'm still trying to wrap my head around it. I haven't been able to focus on it as much as I would like at the moment.

    It would appear that it's above my current level of knowledge, so I'll have to go through it all and try to figure out what's doing what to make sure I get a good grasp of what's going on.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center