Windows Controls: Combo Boxes |
|
Introduction to Combo Boxes |
Overview |
A combo box is a Windows control made of two sections. There are two main types of combo boxes: drop down and simple. Each is made of two sections. The most commonly used combo box is called drop down. On the left side, it is made of an edit box. On the right side, it is equipped with a down-pointing arrow: To use it, the user must click the arrow. This opens a list: After locating the desired item in the list, the user can click it. The item clicked becomes the new one displaying in the edit part of the control. If the user doesn't find the desired item in the list, he or she can click the down-pointing arrow or press Esc. This hides the list and the control displays as before. The user can also display the list by giving focus to the control and then pressing Alt + down arrow key. The second general type of combo box is referred to as simple. This type is also made of two sections but, instead of a down-pointing arrow used to display the list, it shows its list all the time: This time, to select an item, the user can simply locate it in the list and click it. In both types of combo boxes, if the list is too long for the allocated space, when it displays, the list part is equipped with a vertical scroll bar. This allows the user to navigate up and down in the list to locate the desired item: |
Practical Learning: Creating the Application |
#include "resource.h" IDD_CONTROLSDLG DIALOG 260, 200, 180, 120 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Windows Controls" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "Close", IDCANCEL, 120, 100, 50, 14 END
#include <windows.h> #ifdef __BORLANDC__ #pragma argsused #endif #include "resource.h" //--------------------------------------------------------------------------- HWND hWnd; LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); //--------------------------------------------------------------------------- int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { DialogBox(hInstance, MAKEINTRESOURCE(IDD_CONTROLSDLG), hWnd, reinterpret_cast<DLGPROC>(DlgProc)); return 0; } //--------------------------------------------------------------------------- LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam) { switch(Msg) { case WM_INITDIALOG: return TRUE; case WM_COMMAND: switch(wParam) { case IDCANCEL: EndDialog(hWndDlg, 0); return TRUE; } break; } return FALSE; } //---------------------------------------------------------------------------
Creating a Combo Box |
Three are two main ways you can create a combo box. You can write code or use a script. To create a combo box with code, you can first create a Windows class that defines an HWND handle and implements the assignments of a combo box.
The easiest way to create a combo box is through a resource script. The syntax used to create the control in a script is:
COMBOBOX id, x, y, width, height [, style [, extended-style]]
You must specify COMBOBOX as the class of this control
The id is the number used to identify the control in a resource header file
The x measure is its horizontal location with regards to the control's origin, which is located in the top left corner of the window that is hosting the combo box
The y factor is the distance from control's origin, which is located in the top left corner of the window that is hosting the combo box, to the top-left side of the combo box
The width and the height specify the dimensions of the combo box
The optional style and the extended-style factors are used to configure and prepare the behavior of the combo box.
Practical Learning: Creating a Combo Box |
#define IDD_CONTROLSDLG 101 #define IDD_SIZE_CBO 102
#include "resource.h" IDD_CONTROLSDLG DIALOG 260, 200, 180, 120 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Windows Controls" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "Close", IDCANCEL, 120, 100, 50, 14 COMBOBOX IDD_SIZE_CBO, 40, 8, 90, 80 END
Characteristics of a Combo Box |
Windows Styles of a Combo Box |
Like all the other windows, to create a combo box programmatically, you can call the CreateWindow() or the CreateWindowEx() function. The syntax used is:
HWND CreateWindow( "COMBOBOX", "Default String", style, x, y, width, height, parent, menu, instance, Optional Parameter ); |
HWND CreateWindowEx( Extended Style, "COMBOBOX", "Default String", style, x, y, width, height, parent, menu, instance, Optional Parameter ); |
The first argument of the CreateWindow() or the second argument of the CreateWindowEx() functions must be COMBOBOX passed as a string.
The second argument of the CreateWindow() or the third argument of the CreateWindowEx() functions specifies a string that would display in the edit part of the combo box when the control appears. If the control is created with certain styles we will review here, this string would not appear even if you specify it. You can also omit it and pass the argument as NULL or "" since there are other ways you can set the default string.
Like every other Windows control, a combo box' appearance and behavior are controlled by a set of properties called styles . The primary properties of a combo box are those controlled by the operating system and shared by all controls. You can use them to set the visibility, availability, and parenthood, etc, of the combo box. If you create a combo box using a resource script, since you would include it in a DIALOG section of the script, the dialog box is automatically made its parent. Otherwise, to specify that the combo box is hosted by another control, get the handle of the host and pass it as the parent parameter. You must also set or add the WS_CHILD bit value to the style parameter. If you want the combo box to appear when its parent comes up, add the WS_VISIBLE style using the bitwise | operator.
If you want the combo box to receive focus as a result of the user pressing the Tab key, add the WS_TABSTOP style.
The location of a combo box is specified by the x and y parameters whose values are based on the origin, located in the top-left corner or the dialog box or the window that is hosting the combo box.
Practical Learning: Programmatically Creating a Combo Box |
#include <windows.h> #ifdef __BORLANDC__ #pragma argsused #endif #include "resource.h" //--------------------------------------------------------------------------- HWND hWnd; HWND hWndComboBox; HINSTANCE hInst; LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); //--------------------------------------------------------------------------- int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { hInst = hInstance; DialogBox(hInstance, MAKEINTRESOURCE(IDD_CONTROLSDLG), hWnd, reinterpret_cast<DLGPROC>(DlgProc)); return 0; } //--------------------------------------------------------------------------- LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam) { switch(Msg) { case WM_INITDIALOG: hWndComboBox = CreateWindow("COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP, 60, 62, 136, 60, hWndDlg, NULL, hInst, NULL); if( !hWndComboBox ) { MessageBox(hWndDlg, "Could not create the combo box", "Failed Control Creation", MB_OK); return FALSE; } return TRUE; case WM_COMMAND: switch(wParam) { case IDCANCEL: EndDialog(hWndDlg, 0); return TRUE; } break; } return FALSE; } //---------------------------------------------------------------------------
Categories of a Combo Box |
As mentioned already, there are two big categories of combo boxes: simple and drop down. The category is specified by the style of the control. By default, that is, if you don't specify a category, the combo box is created as simple, as you can see on the above screen shot. Otherwise, the style of the simple combo box is CBS_SIMPLE. To create a combo box that display a down-pointing arrow and displays its list only when requested, add the CBS_DROPDOWN instead. Here is an example: CreateWindow("COMBOBOX", NULL, WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWN, 60, 62, 136, 60, hWndDlg, NULL, hInst, NULL); Don't use both styles on the same combo box. A combo box as a Windows control presents many other styles. Most of these styles are related to operations performed on the control. For this reason, we will review them when the relations operations are addressed. |
Practical Learning: Using Combo Box Styles |
#include "resource.h" IDD_CONTROLSDLG DIALOG 260, 200, 180, 120 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Windows Controls" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "Close", IDCANCEL, 120, 100, 50, 14 COMBOBOX IDD_SIZE_CBO, 40, 8, 90, 60, WS_TABSTOP | CBS_DROPDOWN END
Operations on Combo Boxes |
Creation-Related Messages |
When creating the combo boxes above, we specified their location through x and y followed by their dimensions through the width and the height. If a combo box has already been created and you want to get its coordinates, you can send the CB_GETDROPPEDCONTROLRECT message in the SendMessage() function. The third argument, wParam, is not used. The fourth argument, lParam, carries a RECT pointer that will return the coordinates of the combo box. |
Adding Items to the List |
After creating a combo box, the first operation that probably comes in mind is to fill it with items the user would select from. On this issue, there are two types of lists used on combo boxes: regular or owner-draw. A regular list of a combo box displays a normal list of strings. This is the simplest. To create a string to add to the list, you can call the SendMessage() function passing the second argument as CB_ADDSTRING. The syntax used is: lResult = SendMessage( (HWND) hWndControl, (UINT) CB_ADDSTRING, (WPARAM) wParam, (LPARAM) lParam ); The third argument is not used. The fourth argument is the string that will be added to the control. Here are examples: |
//---------------------------------------------------------------------------
LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam)
{
const char *ComboBoxItems[] = { "Sri Lanka", "El Salvador", "Botswana",
"France", "Cuba" };
switch(Msg)
{
case WM_INITDIALOG:
hWndComboBox = CreateWindow("COMBOBOX",
NULL,
WS_CHILD | WS_VISIBLE,
60, 62, 136, 60,
hWndDlg,
NULL,
hInst,
NULL);
if( !hWndComboBox )
{
MessageBox(hWndDlg,
"Could not create the combo box",
"Failed Control Creation",
MB_OK);
return FALSE;
}
SendMessage(hWndComboBox,
CB_ADDSTRING,
0,
reinterpret_cast<LPARAM>((LPCTSTR)ComboBoxItems[0]));
SendMessage(hWndComboBox,
CB_ADDSTRING,
0,
reinterpret_cast<LPARAM>((LPCTSTR)ComboBoxItems[1]));
SendMessage(hWndComboBox,
CB_ADDSTRING,
0,
reinterpret_cast<LPARAM>((LPCTSTR)ComboBoxItems[2]));
SendMessage(hWndComboBox,
CB_ADDSTRING,
0,
reinterpret_cast<LPARAM>((LPCTSTR)ComboBoxItems[3]));
SendMessage(hWndComboBox,
CB_ADDSTRING,
0,
reinterpret_cast<LPARAM>((LPCTSTR)ComboBoxItems[4]));
return TRUE;
case WM_COMMAND:
switch(wParam)
{
case IDCANCEL:
EndDialog(hWndDlg, 0);
return TRUE;
}
break;
}
return FALSE;
}
//---------------------------------------------------------------------------
If the adding operation succeeds, the SendMessage() function returns the index of the item that was added. If it fails, it returns the CB_ERR error. When a list has been created, if it contains more items than the allocated space can display, you should provide a way for the user to navigate entirely in the list. This is usually done with a vertical scroll bar. To equip a combo box with a vertical scroll bar, add the WS_VSCROLL value to its list of styles. If you add this style but the list is not too long, the scroll bar would not display. If you insist on displaying a vertical scroll bar even if the list is not too long, add the CBS_DISABLENOSCROLL style. If you do this and if the list is short, it would appear with a disabled vertical scroll bar:
The CB_ADDSTRING message we use above allows us to add a string to the list. You can use the same message to let the user either create a list or add an item to it. |
Practical Learning: Creating the List |
#define IDD_CONTROLSDLG 101 #define IDD_COUNTRIES_CBO 102
#include "resource.h" IDD_CONTROLSDLG DIALOG 260, 200, 180, 120 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Windows Controls" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "Close", IDCANCEL, 120, 100, 50, 14 LTEXT "Country:", IDC_STATIC, 10, 10, 25, 8 COMBOBOX IDD_COUNTRIES_CBO, 40, 8, 90, 60, WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN END
#include <windows.h> #ifdef __BORLANDC__ #pragma argsused #endif #include "resource.h" //--------------------------------------------------------------------------- HWND hWnd; LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); //--------------------------------------------------------------------------- int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) { hInst = hInstance; DialogBox(hInstance, MAKEINTRESOURCE(IDD_CONTROLSDLG), hWnd, reinterpret_cast<DLGPROC>(DlgProc)); return 0; } //--------------------------------------------------------------------------- LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam) { HWND cboCountries; const char *Countries[] = { "Sri Lanka", "El Salvador", "Botswana", "France", "Cuba", "South Africa", "Australia", "Russia", "Jamaica", "Great Britain", "Senegal", "Bangla Desh" }; switch(Msg) { case WM_INITDIALOG: cboCountries = GetDlgItem(hWndDlg, IDD_COUNTRIES_CBO); for(int Count = 0; Count < 12; Count++) { SendMessage(cboCountries, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)Countries[Count])); } return TRUE; case WM_COMMAND: switch(wParam) { case IDCANCEL: EndDialog(hWndDlg, 0); return TRUE; } break; } return FALSE; } //---------------------------------------------------------------------------
Sorting Items in the List |
As the items are added to the list of a combo box, they create a C++ type of array indexed so that the first item of the list has a numeric position of 0, the second is 1, etc. This index will allow you to perform various operations on items by locating the desired one based on its index.
The items of a combo box are cumulatively added to the list, that is, in order. If you want to arrange them in alphabetical order, when creating the combo box, add the CBS_SORT style. When this style is set, items are automatically and appropriately inserted in the order based on the language of the computer as set in Control Panel. Therefore, every time you add an item to the list using the CB_ADDSTRING message, the list is sorted to rearrange it.
Removing Items from the List |
As opposed to adding an item, if the list already contains strings and you want to remove an item from the list, call the SendMessage() function, passing the message type as CB_DELETESTRING and the index of the undesired item as the lParam value.
Here is an example that deletes the fourth item of the list:
SendMessage(cboCountries, CB_DELETESTRING, 3, 0);
If you want to remove all items from the combo box, call the SendMessage() function with the CB_RESETCONTENT message. The two accompanying arguments, wParam and lParam, are not used.
Selecting an Item |
Once the list of a combo box has been created, the user can click an item in the list to select it. To select an item in a drop down combo box, the user can click the down-pointing arrow or press Alt + down arrow key. Any of these actions causes the list of a drop down combo box to display. You also, as the programmer, can display the list programmatically any time, even if the user clicks another control. To display the list, you can send a CB_SHOWDROPDOWN message in the SendMessage() function. The wParam argument carries a TRUE value if you want to display the list. The lParam argument is not used. Here is an example:
SendMessage(cboCountries, CB_SHOWDROPDOWN, TRUE, 0);
An item that is selected in the list is commonly referred to as the current selection. You also as the programmer can select an item at any time. For example, after creating the list, you may want to specify a default item to be shown in the edit part of the control.
To select an item from the list, you can send the CB_SETCURSEL message in the SendMessage() function. As the items are 0-based, to select a particular item, pass its index as the wParam parameter. The lpParam argument is not used.
Here is an example that selects the fourth item in the list:
SendMessage(cboCountries, CB_SETCURSEL, 3, 0);
An alternative to selecting a string in the combo box is to send a WM_SETTEXT message to the combo box using the SendMessage() function. In this case, the wParam argument is not used. The lParam argument carries the string to select. Here is an example:
SendMessage(cboCountries, WM_SETTEXT, 0, (LPARAM)Countries[5]);
So far, we have mentioned that there were two categories of combo boxes: simple and drop down. We also saw that the user could click the arrow of a drop down style to display the list. The drop down version has another characteristic: it allows the user to click the edit part of the control and start typing. This can let user find a match of a string based on the typed letters. In some cases, you may want the user to only be able to select an item in the list without typing it. In this case, the combo box provides the CBS_DROPDOWNLIST style. When this style has been added, the edit part of the control is disabled or eliminated; it becomes only a place holder for a selected item.
The Width of a Combo Box |
When creating our combo boxes, we learned to specify their width. After a combo box has been created, if you want to find its width, you can call the SendMessage() function with the CB_GETDROPPEDWIDTH message. Both the wParam and the lParam arguments are not used. If the SendMessage() function succeeds in its request, it returns the current width of the combo box.
Sometimes after adding an item to the list, you may find out that it is long than the width of the combo box can display. Although there are various ways you can resize a control, the combo box provides its own and (very) easy mechanism to change its width. This can be done by sending the CB_SETDROPPEDWIDTH message through the SendMessage() function. The wParam argument is used to specify the new desired width. The lParam argument is not used.
SendMessage(cboCountries, CB_SETDROPPEDWIDTH, 240, 0);
Getting the Selected Item |
At any time, to find out what item has been selected or what item is displaying in the edit part of the control, you can call the SendMessage() function with the CB_GETCURSEL message. When the function is called with this message, it checks the combo box first. If an item is currently selected, it returns it. If no item is selected, this function returns CB_ERR.
An alternative to getting the string that is selected in the combo box is to send the WM_GETTEXT to the combo box using the SendMessage() function. The wParam is the length of the string. The lParam argument is the variable into which the string will be copied.
Combo Box Events |
List Display Events |
When a combo box is created as drop down, in order to select an item from the list, the user must display the list. This is done either by clicking the down-pointing arrow or by pressing Alt and the down arrow key (keyboard). However this is done, when the user decides to display the list, just before the list is displayed, the combo box sends a CBN_DROPDOWN message to the dialog box or the object that is hosting the combo box. You can do some last minute processing before the list is displayed. For example, you can intercept this message and prevent the list from displaying, or you can update it, anything.
Since this message comes from a child control, the dialog box processes it as a WM_COMMAND message. The combo box that sends this message can be identified using the low word of the wParam argument of the procedure. In the following example, the message is intercepted and it is used to let the user know before playing the list:
//--------------------------------------------------------------------------- LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam) { HWND cboCountries; const char *Countries[] = { "Sri Lanka", "El Salvador", "Botswana", "France", "Cuba", "South Africa", "Australia", "Russia", "Jamaica", "Great Britain", "Senegal", "Bangla Desh" }; switch(Msg) { case WM_INITDIALOG: cboCountries = GetDlgItem(hWndDlg, IDD_COUNTRIES_CBO); for(int Count = 0; Count < 12; Count++) { SendMessage(cboCountries, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)Countries[Count])); } SendMessage(cboCountries, CB_SETCURSEL, 3, 0); break; case WM_COMMAND: // Windows Controls processing switch(LOWORD(wParam)) // This switch identifies the control { case IDD_COUNTRIES_CBO: // If the combo box sent the message, switch(HIWORD(wParam)) // Find out what message it was { case CBN_DROPDOWN: // This means that the list is about to display MessageBox(hWndDlg, "A request to display the list has been made", "Display Notification", MB_OK); break; } break; case IDCANCEL: EndDialog(hWndDlg, 0); return TRUE; } break; } return FALSE; } //---------------------------------------------------------------------------
We have mentioned that, as opposed to the user displaying the list, you also can display it by sending the CB_SHOWDROPDOWN message. At any time, you can find out whether the list is currently displaying by sending the CB_GETDROPPEDSTATE message to the combo box.
After the user has finished using the list of a combo box, he or she can close it. This can be done by selecting an item, clicking the down-pointing arrow, or pressing Esc. In all cases, this action closes or hides the list and then the combo box sends a CBN_CLOSEUP message to its parent. Here is an example:
//--------------------------------------------------------------------------- LRESULT CALLBACK DlgProc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam) { HWND cboCountries; const char *Countries[] = { "Sri Lanka", "El Salvador", "Botswana", "France", "Cuba", "South Africa", "Australia", "Russia", "Jamaica", "Great Britain", "Senegal", "Bangla Desh" }; switch(Msg) { case WM_INITDIALOG: cboCountries = GetDlgItem(hWndDlg, IDD_COUNTRIES_CBO); for(int Count = 0; Count < 12; Count++) { SendMessage(cboCountries, CB_ADDSTRING, 0, reinterpret_cast<LPARAM>((LPCTSTR)Countries[Count])); } SendMessage(cboCountries, CB_SETCURSEL, 3, 0); break; case WM_COMMAND: // Windows Controls processing switch(LOWORD(wParam)) // This switch identifies the control { case IDD_COUNTRIES_CBO: // If the combo box sent the message, switch(HIWORD(wParam)) // Find out what message it was { case CBN_DROPDOWN: // This means that the list is about to display MessageBox(hWndDlg, "A request to display the list has been made", "Display Notification", MB_OK); break; case CBN_CLOSEUP: MessageBox(hWndDlg, "The list will be closed", "List Close Notification", MB_OK); break; } break; case IDCANCEL: EndDialog(hWndDlg, 0); return TRUE; } break; } return FALSE; } //---------------------------------------------------------------------------
Selection-Related Events |
After the user has selected an item from the list, the combo box sends a CBN_SELCHANGE message to its parent and then the list closes. You can either this event or the CBN_CLOSEUP related event to find out what the user selected, if any.
If the user displays the list and clicks an item, the list is retracted. If the selection was successful (sometimes something could go wrong), the combo box sends a CBN_SELENDOK message. On the other hand, if the user displays the list but doesn't select an item and then either clicks the down-pointing arrow, clicks somewhere else, or presses Esc, the combo box lets its parent know that nothing was selected. To do this, it sends a CBN_SELENDCANCEL message.
|
||
Home | ||
|