Recently I had discussed in a Visual C++ forum about a member's request to implement a custom
ListBox control similar to
CCheckListBox, but with radio buttons. Initially it appeared to be trivial, since the
ListBox control's unique selection version complies with asker requirements, but I have concluded that this control has some advantages:
- It is clearer, with radio buttons, that options are mutually exclusive.
- Is a good alternative to a group of radio buttons, because you have to maintain just one control.
- It inherits some useful features like scrolling, sort and multi-column.
- It will be easier to change options dynamically, as shown in the demo application.
- It will be easier to manage selection events, also shown in the demo application.
Using the Code
CRadioListBox into your project, you just need to do a few steps:
- Include RadioListBox.cpp and RadioListBox.h in your project.
- Insert a
CRadioListBox object into your dialog class declaration (*.h file).
- Put a standard
ListBox control into your dialog's template layout, ensuring that the "owner draw fixed" property is active.
- Create or modify an
OnInitDialog event and subclass a corresponding
For example, if your dialog is named
ListBox member is
m_RadioListBox and the control ID is
IDC_RADIOLISTBOX, then you can subclass the control in the following way:
return TRUE; }
There are other ways to subclass a control in a MFC application, as explained by Eric Sanchez in his article "Control Subclassing," but I think the above version is the shortest one.
As you can see in the above pictures, there is a transparency feature, so the
ListBox can imitate radio button's aspect. This can be easily done by setting the
WS_EX_TRANSPARENCY attribute of the control in the Visual C++ Dialog Editor. Also, it will be necessary to turn
WS_BORDER's style "off."
CRadioListBox class is derived from the
CListBox class with just one derived method:
DrawItem. The method does not highlight the selected item as in a standard
ListBox control, but draws a radio button instead. It also manages the focus state to draw the focus rectangle properly and the background color according to the transparency attribute. I know it could have been better, but this first version runs OK under different screen conditions. Here is the source code:
void CRadioListBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
if (lpDrawItemStruct->itemID == (UINT)-1)
if (lpDrawItemStruct->itemAction & ODA_FOCUS)
int selChange = lpDrawItemStruct->itemAction & ODA_SELECT;
int focusChange = lpDrawItemStruct->itemAction & ODA_FOCUS;
int drawEntire = lpDrawItemStruct->itemAction & ODA_DRAWENTIRE;
if (selChange || drawEntire)
BOOL sel = lpDrawItemStruct->itemState & ODS_SELECTED;
int h =
lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top;
DFCS_BUTTONRADIO | (sel?DFCS_CHECKED:0));
lpDrawItemStruct->rcItem.left += h;
if (focusChange || (drawEntire &&
(lpDrawItemStruct->itemState & ODS_FOCUS)))
To achieve the transparency feature, it will be necessary to control the background painting too, by handling the
HBRUSH CRadioListBox::CtlColor(CDC* pDC, UINT nCtlColor)
if ( (GetExStyle()&WS_EX_TRANSPARENT) && nCtlColor==CTLCOLOR_LISTBOX)
- March 29th, 2005. First version.
- April 6th, 2005. Added transparency feature.