This tutorial will show how use the
CComboBox class in a very basic dialog based application. It will cover the following points:
- Adding a
CComboBox to your dialog
- Changing the height of the Dropdown List
- The difference in behaviour of the 3 styles of
- Inserting/Adding items to the
- Changing the width of the dropdown list
- Using item data
- Determining which item is selected
- Selecting items
A ComboBox is a composite control that consists of an Edit control and a List box control. This tutorial assumes the you are comfortable with creating a dialog based application using the VC++ 6
Class Wizard. If you require information on this topic consult the article A Beginners Guide to Dialog Base Applications - Part 1.
Adding a CComboBox to your dialog
When your dialog-based application is generated, go to the Resources in the Workspace window. Select the dialog
IDD_COMBOBOXTUTORIAL_DIALOG in the Dialog section. To insert the ComboBox control, select it from the Control palette. The cross shaped cursor indicates where the
centre of the edit control portion of the combobox will be placed.
Changing the height of the Dropdown List
Right click on the ComboBox's 'dropdown' button. This will indicate the present size the combo box, including the
extended list box. Then resize it by dragging out the rectangle as you would any other control.
The different in behaviour of the 3 styles of CComboBox
There are three different styles of combo box with different selection properties. To change the style of the combo box Right click on it and select Properties. Then select the Styles tab.
Simple type has a permanently open list box and you are able to type in Edit control.
Dropdown type has a 'closed' list box which can be opened by clicking on the 'drop down' button. The Edit control WILL accept input.
Drop List type has a 'closed' list box which can be opened by clicking on the 'drop down' button. The Edit control will NOT accept any input.
Inserting/Adding items to the CComboBox
It is possible to add items to the combo box at coding time. This is handy for data that will never need to changed. To do this select the Data tab and add the item, one per line. Use Ctrl+Return to go the next line.
It is however more usual and more flexible to load the items into the combo box at run time. This way they can be loaded from resources and be localized or be changed according other user choices.
In the demonstration you will see that Dropdown combo is loaded from string resources, the Drop List initially uses the list that was set up in the Data tab, and the Simple combobox is left empty.
To ensure that the Simple combobox is empty we simply call the
The Dropdown is loaded from string resources by the following for loop, simply call the
AddString() member function of the
for( int i=0; i<6; i++ )
tmp.LoadString(IDS_STRING102 + i);
If you then click on the Insert Long Text Item check box the string "" will inserted after the 3rd Option in the Drop List combo box.
Firstly the index of the string needs to found, and we simply call the
InsertString() member function of the CComboBox class.
int idx = m_DropList.FindString(0, "3rd Option");
The main thing to be careful of when using
InsertString() is that index represents the position before it will be inserted. If the index is
-1 the insert will at the end of the list. (i.e. the same as
Changing the width of the dropdown list
It is best to design the dialog so the combo box can be large enough to fully display the longest option. This is not always possible however. In this case it is handy to widen the drop list so that the entire string can be seen while selecting.
CComboBox class has a function
SetDroppedWidth() for this purpose. This function's input parameter is the outside width of the of the drop list in pixels. First you need the Device Context of the combo box. Then check the length of all the strings in pixels to find the longest.
Note we must also allow for the width of the Scroll bar and window border.
CDC* pDC = m_DropList.GetDC();
int newWidth = 0;
int nWidth = m_DropList.GetDroppedWidth();
for( int i=0; i < m_DropList.GetCount(); i++ )
m_DropList.GetLBText( i, str );
sz = pDC->GetTextExtent(str);
if(sz.cx > newWidth)
newWidth = sz.cx;
newWidth += (GetSystemMetrics(SM_CXVSCROLL) + 2*GetSystemMetrics(SM_CXEDGE));
nWidth = max(nWidth, newWidth);
m_DropList.SetDroppedWidth( nWidth );
Check if the string is longer than the existing drop list, and don't forget to call
m_DropList.GetWindowRect( &rc ); will return the width of the edit box and can therefore be used to return the drop list to it's original size.
When using a simple it best to set the Auto Horizontal scroll Style.
Determining which item is selected
There are two ways to determine which item is selected. You can use Class Wizard to associated a control and data
variable with the control.
Also add an
OnSelChanged function for the combo box with Class Wizard.
Drop List combo boxes can have an integer data variable which returns the index of the selected item, and the code be as simple as below.
transfers the index to the variable.
if( m_nDropListIndex < 0 ) return;
m_DropList.GetLBText( m_nDropListIndex, str );
Out.Format( "Drop List Selection => index %d\n%s", m_nDropListIndex, str );
AfxMessageBox( Out );
DropDown and Simple combo boxes can have a
CString data variable which returns the selected string. To get the string you can again call
UpdateData();, or call
int idx = m_Simple.GetCurSel();
if( idx < 0 ) return;
m_Simple.GetLBText( idx, str );
Out.Format( "Drop List Selection => index %d\n%s", idx, str );
AfxMessageBox( Out );
You will notice that Class Wizard will only allow you attach an integer variable to a DropList Combo box and a
CString variable to the DropDown and Simple Combo boxes.
You can add the Data Exchange entry to attach the alternate variable type manually, but the entry and the variable declaration must be placed outside the
AFX_DATA tags so that Class Wizard won't remove them.
DDX_CBIndex(pDX, IDC_COMBO2, m_nDropListIndex);
DDX_CBString(pDX, IDC_COMBO2, m_strDropList);
Check the string in
CBN_SELCHANGE handler after the call to
Selecting an item can be done by setting the index as below. If you use the integer variable call
You can use
SetCurSel( index )
SelectString( after, str ) where after is the index after which to start searching. As you can see from the demo the items are added in a different order to which they are displayed and the data remains matched.
m_nDropListIndex = 2;
If you know that the index can never be set to one lager than the number of items in the list the code can be left as it is. If there is a risk of this however then it is wise to check and either set the index to a default item or send a message box to the user waring of the error.
if( m_Simple.GetCount() <= 2 )
AfxMessageBox( "Index is out of range, selecting default" );
Using item data
A combo box returns a zero based index of the selected item or the string as shown above. Often the program actually uses a value other than the string or the index, and what's more you need to get this value correct whether the items in the list are sorted or not. If the application is to support multiple languages this is even more important because sorting can be different.
To assists with this each item in the list can have a
DWORD associated with it, and this remains attached to the string regardless of the sorting.
SetItemData( idx, dwData ) and
GetItemData( idx ). When setting the data use the index returned by the
InsertString() functions to ensure you get it right.
int idx = m_DropDown.AddString( str.Left(pos));
m_DropDown.SetItemData( idx, dw );
To retrieve the data get the item index and call
int idx = m_DropDown.GetCurSel();
DWORD dw = m_DropDown.GetItemData( idx );
Handling CComboBox messages
The notification messages available for combo boxes are the following
CBN_ERRSPACE notification message is sent when a combo box cannot allocate enough memory.
CBN_SELCHANGE notification message is sent when the user changes the current selection in the list box of a combo box.
CBN_DBLCLK notification message is sent when the user double-clicks a string in the list box of a combo box.
CBN_SETFOCUS notification message is sent when a combo box receives the keyboard focus.
CBN_KILLFOCUS notification message is sent when a combo box loses the keyboard focus.
CBN_EDITCHANGE notification message is sent after the user has taken an action that may have altered the text in the edit control portion of a combo box.
CBN_EDITUPDATE notification message is sent when the edit control portion of a combo box is about to display altered text.
CBN_DROPDOWN notification message is sent when the list box of a combo box is about to be made visible.
CBN_CLOSEUP notification message is sent when the list box of a combo box has been closed.
CBN_SELENDOK notification message is sent when the user selects a list item, or selects an item and then closes the list.
CBN_SELENDCANCEL notification message is sent when the user selects an item, but then selects another control or closes the dialog box.
The demo handles the
CBN_EDITUPDATE messages. The
CBN_EDITUPDATE is handy if you like to prevent some characters from being typed.
The code below is for demonstration only and this kind of validation is best carried out by the control itself. This would involve subclassing the control and overriding of some functions. This more complex topic is discussed elsewhere on this site.
DWORD dwSel = m_Simple.GetEditSel();
if( LOWORD( dwSel ) != LOWORD( dwSel ) ) return;
if( ispunct( m_strSimple[LOWORD( dwSel )-1] ))
m_strSimple = m_strSimple.Left(LOWORD( dwSel )-1) + m_strSimple.Mid(LOWORD( dwSel ));
m_Simple.SetEditSel( LOWORD( dwSel ), LOWORD( dwSel ) );
A word or two of caution. Validation can also be done in the
CBN_KILLFOCUS handler. If you do this take care in the way you change incorrect data back and pop up message boxes that also take focus from the control. The program can finish up in an unfortunate loop of validation and message box display.
Some code you may like to play with, is to vary the number of lines of items displayed in the drop down list. This will prevent empty lines at the end of the list and change the height from what was set in the resource editor. The framework does handle some of these things already but sometimes it good to have the control.
void CComboBoxTutorialDlg::SetNumberOfLines(int nLines)
nLines = min( max(nLines, 1), 7 );
m_DropDown.GetWindowRect( &lprect );
lprect.bottom = lprect.top + nLines * m_DropDown.GetItemHeight( -1 ) + lprect.Height();
m_DropDown.SetWindowPos(NULL, 0, 0, lprect.Width(), lprect.Height(), SWP_NOMOVE | SWP_NOZORDER );
ENTER key, by default, is only processed by the combo box when the drop list is open, and in this case it has the same effect as a mouse click and makes the selection. A Simple combo box does not process the
ENTER key at all and is passed to the parent dialog. If you wish to handle the
ENTER key in any other way the combo box will need to be subclassed and handler written for the purpose. You may like to check out Implementing an autocompleting Combobox - By Chris Maunder as a starting point to which the handler can be added.
The Message handlers in this tutorial are the most commonly used and should indicate that they need not be very complex. More complex issues are handled elsewhere on this site.
Started working with computers when I/O was with punched paper tape, using Algol. Then learned Fortran, Basic, various Assemblers, Forth and Postscript. Built a robot in the early '80s. When I finally got a PC I learned C, C++ and more recently worked on a variety of .NET and PHP projects. Senior Software Engineer at MedTech Global in Melbourne, Australia.