News!
The source code and the related bugs for this article can now be found at Google Code: http://code.google.com/p/advcombobox/.
I moved it there so everyone can report bugs and pitch in to help the development of the control. I don't have the time to fix everything myself, so if you want to help out, please send a mail to mathias.tunared@gmail.com, and I'll add you to the project. I will continue to update this page here at CodeProject.com for every new release. This control has been here for so long, and it is still used, so I think it's about time I make an update.
AdvComboBox Article Index
Introduction Installation Functions Notifications Styles Future upgrades Hints & Tips Improvements and Bug Fixes
What's new in Version 2.1
I got really tired of having to add all items in the InitDialog function, which can be really annoying in the case where you have several CAdvComboBox
in one page. This class will now look for an entry in the stringtable with the same ID as the CAdvComboBox
control has in the resource editor. The demo project has been upgraded to display this feature. More information about this has been added under Installation. If you want to add another stringtable entry, just call the new function LoadString(UINT nID)
where nID refers to the stringtable entry.
In this version, I have added the code so the CAdvComboBox
can be used with the macro RUNTIME_CLASS
. Requested by Krishna.
I've also added the missing functionality that opens the dropdown window by pressing Alt-Down or Alt-Up. This was requested by Thomas Freudenberg.
I started to work on a control that I was going to use in another program that had combo boxes within a CListBox
. The first criteria that I wanted was that the combobox should be flat (no shadows). I looked around for a while and could not find any suitable so I had to create my own somehow. When I had started to work on the flat combobox, I come to think of another cool feature that would be nice to have. How many times have you wanted the same functionality as the address bar in IE. When you type an address and the IAutoComplete
COM-object goes to work. Wonderful!...or not. I wanted the same functionality in a my combobox, and especially the resizeable dropdown window. And so, back to the drawing board. After many long nights and several different approaches, I got it to work.
This ComboBox
is not subclassing the MFC's CComboBox
mostly because I wanted to be able to resize the drop-down window. Instead, I created a CWnd
object that looks almost the same as MFC's CComboBox
. The tricky part was to get the dropdown window to react the same way as CComboBox
. First of all, the dropdown window has to be a child to the desktop, and then there is the nasty problem with the mouse and keyboard capture. The solution to that problem was to have one window that has a listbox and a scrollbar within itself. The scrollbar that the CListBox
class uses could not be used due to the capture problem.
During the development more features were added; checked items and disabled items in the dropdown window.
Below is a picture over the class structure.
Picture 1
The dropdown window of the AdvComboBox
is of an autosize style, that is, it calculates the size it needs to show all items in the list.
So the result is a combobox that acts almost the same as the MFC's CComboBox
. You should not have to change any code that are already in use, besides that you have to change the resource from the standard combobox to a custom control. I have tested on W2K and XP. Now I turn to CodeProject's members and other to help me test and find bugs, and maybe some new features to add.
Installation Index
Create a new project
Upgrade your current project
Use CAdvComboBox in a MFC DLL
This article contains of three different ways of implementing the CAdvComboBox
class into your project. One describes the workorder for a new project, and the second how to upgrade your current projects, and the last how to implement the CAdvComboBox
in a MFC DLL.
- Create a dialog application using the App Wizard.
The explanation below are built upon that you call your application AdvCBDemo - Insert the following files to your workspace
AdvComboBox.h
AdvComboBox.cpp
DropWnd.h
DropWnd.cpp
DropListBox.h
DropListBox.cpp
DropScrollBar.h
DropScrollBar.cpp
VisualStylesXP.h
VisualStylesXP.h
- Include the file AdvComboBox.h in your AdvCBDemoDlg.h file
- Add a custom control to your dialog
In resource editor, place a new custom control in the dialog by using the Custom Control tool in the toolbar. Enter the properties for the control as shown in the picture below.
The CAdvComboBox
custom control class name is AdvComboBoxCtrl
which is defined in AdvComboBox.h.
Picture 2
Descriptions of the different styles to set:
Combo style | Value in property window |
CBS_DROPDOWN | 0x_____002 |
CBS_DROPDOWNLIST | 0x_____103 |
CBS_AUTOHSCROLL | 0x_____043 |
CBS_SORT | 0x_____103 |
So this means that if you want your combobox to have the styles CBS_DROPDOWN
, CBS_AUTOHSCROLL
, and CBS_SORT
the style value should end with 142(as seen in picture 2). You can also edit the resource file in text mode and add the styles there. The resouce editor then translate the style you have choosen into this value.
The following styles has not been implemented yet:
CBS_DISABLENOSCROLL
CBS_HASSTRINGS
CBS_LOWERCASE
CBS_NOINTEGRALHEIGHT
CBS_OEMCONVERT
CBS_OWNERDRAWFIXED
CBS_OWNERDRAWVARIABLE
CBS_SIMPLE
CBS_UPPERCASE
- Add the following public member variable to your CAdvCBDemoDlg class in the AdvCBDemoDlg.h header file:
CAdvComboBox m_ctlAdvCombo;
- Add the following DXX_ call in your AdvCBDemoDlg.cpp file
DDX_Control(pDX, IDC_ADV_COMBO, m_ctlAdvCombo);
Add it outside of AFX_DATA_MAP
to avoid any mishaps in the future.
Now, the function DoDataExchange(...)
should look like this:
void CAdvCBDemoDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Control(pDX, IDC_ADV_COMBO, m_ctlAdvCombo);
}
- Compile and run
You should now be able to test your dialog with the AdvComboBox within it.
Another feature that still has to be implemented is how to get and set data from the control. This is described below.
- Set and get data from AdvComboBox
You can use the same way to get and set data as you would with a normal CComboBox, except one small difference.
The DDX_CBIndex
and DDX_CBString
should be changed to DDX_ACBIndex
and DDX_ACBString
for it to work. These functions can be found in the AdvComboBox.cpp file.
To add functionality to get and set data add one of, or both, the following variables in your AdvCBDemoDlg.h file
int m_nAdvCombo;
CString m_strAdvCombo;
You also have to initialize the value to these variables in the constructor of CAdvCBDemoDlg
. Add the following code outside AFX_DATA_INIT
.
m_nAdvCombo = -1;
m_strAdvCombo = "";
Add also the DDX_ calls in the function DoDataExchange(...)
in AdvCBDemoDlg.cpp file below the DDX_Control
that you added in step 6.
DDX_ACBIndex( pDX, IDC_ADV_COMBO, m_nAdvCombo );
DDX_ACBString( pDX, IDC_ADV_COMBO, m_strAdvCombo );
You should now be able to use the AdvComboBox control in the same way as the MFC's CComboBox.
The following was added in version 2.1
- Use a stringtable entry to populate the
CAdvComboBox
list.
To be able to use the stringtable with this combobox, a stringtable entry has to be created with the same identifier as the control was created with in the resource editor. In this case, the stringtable entry ID should be IDC_ADV_COMBO. Below is a picture (picture 3) of this example that you can add to your project.
In the stringtable entry, a newline ('\n'
) represents the ending of one item in the combobox list. Please look into the demo project for further information on how to implement this.
Picture 3
This description is not so detailed as the one above. Hopefully you have enough knowledge to manage the following.
- Insert the following files to your workspace
AdvComboBox.h
AdvComboBox.cpp
DropWnd.h
DropWnd.cpp
DropListBox.h
DropListBox.cpp
DropScrollBar.h
DropScrollBar.cpp
VisualStylesXP.h
VisualStylesXP.h
- Include the file AdvComboBox.h in your header file
- Change classnames
Rename all of your CComboBox
variable definitions to CAdvComboBox
in your header file. - Replace resource comboboxes
Replace your current comboboxes in the resource editor to custom controls and set the properties as shown in picture 2. You can of course change the style of the custom control.
Tip! Use the same ID for your custom controls as you had for the comboboxes. This makes it a lot easier further on so that you won't have to change ID's for all your events(ON_CBN_SELCHANGE
, etc.) currectly assigned to the standard comboboxes.
- Change DDX_ calls
Change all DDX_CBIndex
and DDX_CBString
to DDX_ACBIndex
and DDX_ACBString
in your .cpp file. Change the ID's in these calls if you didn't set the same ID's on your new custom controls as you had on the old comboboxes. - Compile and test
Your project should now work except the limitations of the CAdvComboBox
.
This is just a small description of how to use this class in a DLL that has windows(dialogs) in the DLL, and those windows uses
CAdvComboBox
. To be able to register the class, you have to change the code somewhat. The
HINSTANCE
in the structure
WNDCLASS
must have the DLL's
HINSTANCE
, and not the calling applications
HINSTANCE
.
- Declare a CAdvComboBox variable
Create the class like this:
CAdvComboBox m_ctlAdvCombo( TRUE )
- Global HINSTANCE variable
You also have to add a global HINSTANCE
variable the the DLL project.
Add the following to, for example, StdAfx.cpp in your DLL project:
HINSTANCE g_hDLLInstance = NULL;
Then, add the extern
definition in the StdAfx.h file like this:
extern HINSTANCE g_hDLLInstance;
- Initialize the global HINSTANCE
Set the hDllInstance
in the DllMain function.
g_hDLLInstance = hInstance;
- Change code in CAdvComboBox
Next thing is to find the function RegisterWindowClass()
in the AdvComboBox.cpp file. Change the function so it looks like this:
BOOL CAdvComboBox::RegisterWindowClass()
{
WNDCLASS wndcls;
HINSTANCE hInst;
if( m_bInst )
{
hInst = g_hDLLInstance;
}
else
{
hInst = AfxGetInstanceHandle();
}
ASSERT( hInst != 0 );
...
return TRUE;
}
- Export the CAdvComboBox class
Last, but not least, you have to make the class exported if you want to be able to use it outside the DLL. Remember to make the two DDX_ functions exported too. Class definition:
class __declspec(dllexport) CAdvComboBox : public CWnd
DDX_ Definitions:
_declspec(dllexport) void AFXAPI DDX_ACBIndex( CDataExchange* pDX,
int nIDC, int& index );
_declspec(dllexport) void AFXAPI DDX_ACBString( CDataExchange* pDX,
int nIDC, CString& value );
The functions in
CAdvComboBox
class are the same as in MFC's
CComboBox
. For mor help on those, look in MSDN.
There are also some extra functions that can be used. These are described below
- GetComboRect
Description: | Retrieve the pos and size of the combobox with this function |
Definition: | CRect& GetComboRect() |
Returns: | Rect of the AdvComboBox |
- GetDefaultVisibleItems New in ver 2.0
Description: | Returns the default number of visible items of the dropdown window. To set the default number, use SetDefaultVisibleItems(int) |
Definition: | int GetDefaultVisibleItems() |
Returns: | Number of items. |
- GetItemChecked
Description: | Get the checked status of an item |
Definition: | BOOL GetItemChecked( int nIndex ) |
Parameter: | nIndex Item index in list |
Returns: | TRUE if item nIndex is checked, or CB_ERR if an error occurred. |
- GetItemDisabled
Description: | Get the disabled status of an item |
Definition: | BOOL GetItemDisabled( int nIndex ) |
Parameter: | nIndex Item index in list |
Returns: | TRUE if item nIndex is disabled, or CB_ERR if an error occurred. |
- GetMinVisibleItems New in ver 1.2
Description: | Get the minimum visible listbox items in the dropwindow, before the dropwindow will be placed above the combobox. |
Definition: | int GetMinVisibleItems() |
Returns: | Number of minimum visible listbox item. |
- LoadString New in ver 2.1
Description: | Load an entry from the stringtable and add the items in that entry in the dropwindow list. |
Definition: | void LoadString( UINT nStringID ) |
Parameter: | nStringID ID of the string to load. |
Returns: | void |
- ModifyACBStyle
Description: | Modify the style of the CAdvComboBox. The available styles can be found here. This function handles only the ACBS_ styles. It work as MFC's ModifyStyle . |
Definition: | void ModifyACBStyle(UINT nRemoveStyle, UINT nAddStyle) |
Parameter: | nRemoveStyle ACBS style to remove. |
Parameter: | nAddStyle ACBS style to add. |
Returns: | void |
- PointInWindow
Description: | Is a CPoint within the combobox rect? |
Definition: | BOOL PointInWindow( CPoint ptScreenPoint ) |
Parameter: | ptScreenPoint Is this point within the combobox. |
Returns: | TRUE if the point is in combobox window. |
- SetDefaultVisibleItems New in ver 2.0
Description: | Set the default visible number of items in the dropdown window. |
Definition: | void SetDefaultVisibleItems( int nItems = -1 ); |
Parameter: | nItems Number of items to be visible when the dropdown window is shown. Set this to -1 if the style should be autosize. |
Returns: | void |
- SetItemChecked
Description: | Set an item in combo to either check or un-checked. |
Definition: | void SetItemChecked( int nIndex, BOOL bChecked ) |
Parameter: | nIndex Zero-based item index to the item to set. |
Parameter: | bChecked New state of the item to set. |
Returns: | void |
- SetItemDisabled
Description: | Set an item in combo to either disabled or enabled. |
Definition: | void SetItemDisabled(int nIndex, BOOL bDisabled) |
Parameter: | nIndex Zero-based item index to the item to set. |
Parameter: | bDisabled New state of the item to set. |
Returns: | void |
- SetMinVisibleItems New in ver 1.2
Description: | Set the minimum visible listbox items in the dropwindow, before the dropwindow will be placed above the combobox. The default is five items. |
Definition: | void SetMinVisibleItems(int nMinItems) |
Parameter: | nMinItems Number of minimum listbox items. |
Returns: | void |
- DDX_ACBIndex
Description: | Use this function to get and set data with MFC's UpdateData(...) |
Definition: | void AFXAPI DDX_ACBIndex( CDataExchange* pDX, int nIDC, int& index ) |
- DDX_ACBString
Description: | Use this function to get and set data with MFC's UpdateData(...) |
Definition: | void AFXAPI DDX_ACBString( CDataExchange* pDX, int nIDC, CString& value ) |
Below is a list of CComboBox
functions implemented in CAdvComboBox
.
AddString
DeleteString
FindString
FindStringExact
GetCount
GetCurSel
GetDroppedControlRect
GetDroppedState
GetEditSel
GetItemData
GetItemDataPtr
GetLBText
GetLBTextLen
GetTopIndex
InsertString
LimitText
New in ver 1.2 ResetContent
SelectString
SetCurSel
SetEditSel
SetItemData
SetItemDataPtr
SetTopIndex
ShowDropDown
The following CComboBox
functions are not implemented in CAdvComboBox
Clear
CompareItem
Copy
Cut
Dir
DrawItem
DeleteItem
GetDroppedWidth
GetExtendedUI
GetHorizontalExtent
GetItemHeight
GetLocale
MeasureItem
Paste
SetDroppedWidth
SetExtendedUI
SetHorizontalExtent
SetItemHeight
SetLocale
The
CAdvComboBox
sends the following notifications to the parent, as MFC's
CComboBox
does.
CBN_CLOSEUP
CBN_DROPDOWN
CBN_EDITCHANGE
CBN_EDITUPDATE
CBN_KILLFOCUS
CBN_SELCHANGE
CBN_SELENDCANCEL
CBN_SELENDOK
CBN_SETFOCUS
The following notifications are not sent.
CBN_DBLCLK
This message is only sent when the combobox style is CBS_SIMPLE
and CAdvComboBox
does not support this style. CBN_ERRSPACE
There are no memory management built into this control yet, and I'm not sure if I'll ever do that. Boring!
The
CAdvComboBox
has its own styles. They are described here.
ACBS_FLAT
Makes the combobox flat. With this style the combobox will be drawn all the way out to the borders of the custom control rect in the resources.ACBS_STANDARD
With this style the combobox will be drawn as an ordinary combobox, i.e. 3D look.ACBS_CHECKED
To make the dropdown window contain a checkable list, use this style.ACBS_AUTOAPPEND
The AdvComboBox will automaticallly search the dropwindow items after the nearest match to the text that has been entered in the editbox of the AdvComboBox. If found, it will then append the remaining text to the editbox.ACBS_AUTOSUGGEST
While entering text in the editbox, the AdvComboBox will display a list in the dropdown window the matches the entered text.
The CAdvComboBox
supports the following CComboBox
styles
CBS_AUTOHSCROLL
CBS_DROPDOWN
CBS_DROPDOWNLIST
CBS_SORT
The following CComboBox
styles are not supported
CBS_DISABLENOSCROLL
CBS_HASSTRINGS
CBS_LOWERCASE
CBS_NOINTEGRALHEIGHT
CBS_OEMCONVERT
CBS_OWNERDRAWFIXED
CBS_OWNERDRAWVARIABLE
CBS_SIMPLE
CBS_UPPERCASE
Version 2.1
- Stringtable entry with same ID will populate the list.
- Added function
LoadString(UINT)
- Macro
RUNTIME_CLASS
can now be used.
In this version, I have added to the code so the CAdvComboBox
can be used with the macro RUNTIME_CLASS
. Requested by Krishna. - Pressing Alt+Up/Down opens the dropdown window.
Added the missing functionality that opens the dropdown window by pressing Alt-Down or Alt-Up. This was a request from Thomas Freudenberg.
Version 2.0
- Added function
GetDefaultVisibleItems()
- Added function
SetDefaultVisibleItems(int)
- Added
CAdvComboBox
style ACBS_AUTOAPPEND
- Added
CAdvComboBox
style ACBS_AUTOSUGGEST
- The
CAdvComboBox
is now also compatible with CommCtrl 6.0 manifest.
I used David Yuheng Zhao's class CVisualStylesXP
as the working ground, but I have added some functionality like the functions fetched from the DLL is now static functions, and a function that checks the CommCtrl DLL version to see if we are able to use visual styles. If a manifest is not used, this function will tell us just that even if we're running on WinXP. - Added two new files: VisualStylesXP.h and VisualStylesXP.cpp
Version 1.21
- Fixed a bug when using Create(...)
The bug appeared when trying to create the CAdvCombBox
class with the function Create(...)
and the style was CBS_DROPDOWN
. Thanx for finding this bug.
Version 1.2
- Scrolling the dropwindow with the mousewheel is implemented.
- The
CComboBox
function LimitText
is now implemented. - The need for Windows definition
_WIN32_WINNT
is removed. - Dropwindow above control
The dropdown window will be shown above the combobox if the distance to the bottom of the screen is not enough. The default minimum number of listbox items is set to five, but this value can be changed by calling the function SetMinVisibleItems(int)
in CAdvComboBox
class.
Version 1.1
- The control can now be disabled.
- Switching to another program when the dropwindow is down closes the dropwindow.
- Corrected a minor drawing error under Win2K.