Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

CRadioListBox: A ListBox with Radio Buttons (MFC version)

0.00/5 (No votes)
22 Dec 2007 1  
How to implement an owner-drawn ListBox with radio buttons instead of standard selection highlight

Introduction

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

To implement 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 ListBox.

For example, if your dialog is named CMyDialog, the ListBox member is m_RadioListBox and the control ID is IDC_RADIOLISTBOX, then you can subclass the control in the following way:

BOOL CMyDialog::OnInitDialog()
{
    CDialog::OnInitDialog();

    // Some other custom source code here

    m_RadioListBox.SubclassDlgItem(IDC_RADIOLISTBOX, this);   
    
    return TRUE;  // return TRUE  unless you set the focus to a control
}

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.

Transparency

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 Internals

The 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);
    // just draws focus rectangle when listbox is empty
    if (lpDrawItemStruct->itemID == (UINT)-1)
      {
         if (lpDrawItemStruct->itemAction & ODA_FOCUS)
         pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
         return;
      }
    else
      {
         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;
               // Draws background rectangle, color depends on transparency
               pDC->FillSolidRect(&lpDrawItemStruct->rcItem, 
               ::GetSysColor((GetExStyle()&WS_EX_TRANSPARENT)?
                                                COLOR_BTNFACE:COLOR_WINDOW));
               // Draw radio button
               int h = 
                 lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top;
               CRect rect(lpDrawItemStruct->rcItem.left+2, 
                          lpDrawItemStruct->rcItem.top+2, 
                          lpDrawItemStruct->rcItem.left+h-3, 
                          lpDrawItemStruct->rcItem.top+h-3);
               pDC->DrawFrameControl(&rect, DFC_BUTTON, 
                       DFCS_BUTTONRADIO | (sel?DFCS_CHECKED:0));
               // Draws item text
               pDC->SetTextColor(COLOR_WINDOWTEXT);
               pDC->SetBkMode(TRANSPARENT);
               lpDrawItemStruct->rcItem.left += h;
               pDC->DrawText((LPCTSTR)lpDrawItemStruct->itemData, 
                            &lpDrawItemStruct->rcItem, DT_LEFT);
         }
         // draws focus rectangle
         if (focusChange || (drawEntire && 
                  (lpDrawItemStruct->itemState & ODS_FOCUS)))
             pDC->DrawFocusRect(&lpDrawItemStruct->rcItem);
     }
}

To achieve the transparency feature, it will be necessary to control the background painting too, by handling the WM_CTLCOLOR message:

HBRUSH CRadioListBox::CtlColor(CDC* pDC, UINT nCtlColor) 
{
    // If transparent style selected...
    if ( (GetExStyle()&WS_EX_TRANSPARENT) && nCtlColor==CTLCOLOR_LISTBOX)
        return (HBRUSH)::GetSysColorBrush(COLOR_BTNFACE);

    return NULL;
}

History

  • March 29th, 2005. First version.
  • April 6th, 2005. Added transparency feature.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here