Radio Buttons in MFC Dialogs

Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handling this, but the documentation is a bit unclear, and it helps to see the whole system in action.  

I'll also show a useful technique of creating an enumeration variable to simplify identification and processing of the selected option.  And I'll describe how to disable sub-items that are related to just one of the radio-button options to give your U/I that truly professional look.
Example Dialog or FormIn a "settings" dialog, you will typically need to present your user with several options related to a particular activity.  When you have a largish number, say 4 or 5, then you will display a listbox or a combobox -- especially if that set of options might grow in future versions of your program.  

But when there are only two or three options, it's usually best to show a set of Radio Buttons in your dialog (BTW... Never use a radio button when there is only one option -- use a checkbox instead).

MFC provides the tools, but the Wizard support is a bit finicky -- you need to do things in the right order if you want the Wizard to work for you,  You can also add the radio-group handling manually, but it's worth knowing The MFC way.


Use the Dialog Editor to design the form.
In the example, there are three radio buttons.  There are some additional settings related to only the third option ("Custom").


Set the Tab Order.
It's important that the radio buttons are numbered sequentially.  To set the tab order, use the menu command
    Format / Tab Order (Ctrl+D)  
Click on each control in the order you want them.  In particular, make sure that the radio buttons have sequential numbers.
Setting the Tab Order


Set the Group attribute to True for the first button only.  
Click on the other items to verify that "Group" is set to False.
Important: Set the FIRST one to "Group"


You need to add a DDX_Radio line to the DoDataExchange function.  Let's let the Wizard do it for you.  
In the Dialog Editor:
Right-click the top radio button and choose Add Variable... Set (in this order):
        Category:  Value
  Variable type:  int
Variable name:  m_eRdoGrpFrab
Adding an 'int' Value Variable to the DialogStep 3 is key, because the "int" option is not available unless the "Group" attribute has been set!

My DoDataExchange function now looks like:
void CMyOptsDlg::DoDataExchange(CDataExchange* pDX)
                      	DDX_Radio(pDX, IDC_RADIO1, m_eRdoGrpFrab);  // <<--- Note
                      	DDX_Text(pDX, IDC_EDIT3, m_sLogFile);

Open in new window

You now have a member variable named m_eRdoGrpFrab that can be set to 0, 1, or 2 (Standard, Partial, or Custom).  When you run the dialog, you'll simply set it like so:
	CMyOptsDlg dlg;
                      	dlg.m_eRdoGrpFrab= 0; // or 0,1, or 2
                      	int nResp= dlg.DoModal();
                      	if (nResp == IDOK) {
                      		... etc...

Open in new window

And when the dialog is closed, that variable will be set with the new, user-selected value (0, 1, or 2).

[step="" title="An Additional Slightly Advanced (but very useful) Technique"][/step]You can use the literal values 0,1, and 2 in your programming, but this is a perfect time to use the more advanced technique of setting up an enumerated datatype; there are only three options, so give each one a name.  It makes your code self-documenting and it's cool to see the names (rather than arbitrary integers) when debugging.  Add this to the header for the dialog:
	//int m_eRdoGrpFrab;  // the Wizard added this... change to:
                      	typedef enum { FrabStd=0, FrabPartial=1, FrabCustom=2 } FrabOpts;
                      	FrabOpts m_eRdoGrpFrab;

Open in new window

...and change the DDX_Radio line in DoDataExchange() to:
  DDX_Radio(pDX, IDC_RADIO1, (int&)m_eRdoGrpFrab);  // <<--- Note (int&) cast

Open in new window

Now you can use code like:
	dlg.m_eRdoGrpFrab= CMyOptsDlg::FrabPartial;
                      	if (dlg.m_eRdoGrpFrab==CMyOptsDlg::FrabCustom ) {

Open in new window

[step="" title="About that Automatic Enabling..."][/step]Looking at the example in Figure 1-1, note that when the first or second radio button is selected, input items relating to "Custom" are disabled.  This is good U/I design.  The user can't modify these settings because they make no sense unless Custom is selected.  And you should want to disable the labels, as well as the controls.  That sort of little enhancement is what separates the high-paid, top-tier programmers from the novices who get laid off in the first downsizing pass.  Which one do you want to be? :-)

In order to disable those other controls, do the following:  
1) First, make sure the tab order is correct/sequential for the entire set.  
2) Give a symbolic name (other than IDC_STATIC) to the first and last label.
3) Double-click each radio button to have the wizard provide an OnClick handler.  
I usually collapse my code so that it looks like this:
void CMyOptsDlg::OnBnClickedRadio1(){ DoEnabling(); }
                      void CMyOptsDlg::OnBnClickedRadio2(){ DoEnabling(); }
                      void CMyOptsDlg::OnBnClickedRadio3(){ DoEnabling(); }

Open in new window

I long-ago wrote a function that would let me easily disable (and enable) a sequential range of dialog controls.  Here's that code, along with the code for DoEnabling:
//--------------------- Utilities for disabling a range of dialog items (labels and controls)
                      void EnableDlgItm( CDialog* pDlg, int nId, BOOL fEnable/*=TRUE*/ )
                          CWnd* pwnd= pDlg->GetDlgItem( nId );
                          if (pwnd ) {
                              pwnd->EnableWindow( fEnable );
                      void EnableDlgItmRange( CDialog* pDlg, int nIdFirst/*=-1*/, int nIdLast/*=-1*/, BOOL fEnable/*=TRUE*/ )
                          CWnd* pwnd;
                          if ( nIdFirst == -1 ) { // IDC_STATIC
                              pwnd= pDlg->GetWindow( GW_CHILD );   // first Child
                              nIdFirst= pwnd->GetDlgCtrlID();
                          pwnd= pDlg->GetDlgItem( nIdFirst );
                          while ( pwnd ) {
                              pwnd->EnableWindow( fEnable );
                              pwnd= pwnd->GetWindow( GW_HWNDNEXT ); 
                              if ( pwnd ) pwnd->EnableWindow( fEnable );
                              if ((nIdLast != -1) && (pwnd && pwnd->GetDlgCtrlID() == nIdLast)) {
                      //--------------------------- Using this in the dialog
                      void CMyOptsDlg::DoEnabling() 
                          UpdateData(TRUE);  //--- find out what's been selected
                          //----------------------------- disable some items when "Custom" is not selected
                          BOOL fEnab= (m_eRdoGrpFrab == FrabCustom); 
                          EnableDlgItmRange( this, IDC_LblCustomStart, IDC_LblCustomEnd, fEnab );

Open in new window

And one last thing:
4) In your OnInitDialog function make a call to the DoEnabling() function.

The MFC coders at Microsoft implemented support for automatically handling a group of radio buttons.   This Article has described how to use that support quickly and easily.  Knowing how the MFC Wizard works will make these U/I design and implementation tasks easier.   And when you know the system, you can use more advanced techniques to make your forms more professional.

If you liked this article and want to see more from this author,  please click the Yes button near the:
      Was this article helpful?
label that is just below and to the right of this text.   Thanks!

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.