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

AdvComboBox - Version 2.1

0.00/5 (No votes)
29 Sep 2010 33  
A Combobox in which the user can resize the drop window. Can be standard style or flat style.

Sample Image - CAdvComboBox

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.

Introduction

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.

Class overview
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

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 new project

  1. Create a dialog application using the App Wizard.
    The explanation below are built upon that you call your application AdvCBDemo
  2. 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

  3. Include the file AdvComboBox.h in your AdvCBDemoDlg.h file
  4. 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.

    Custom control properties
    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

  5. Add the following public member variable to your CAdvCBDemoDlg class in the AdvCBDemoDlg.h header file:
    CAdvComboBox	m_ctlAdvCombo;
  6. 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:
    "cpp-keyword">void CAdvCBDemoDlg::DoDataExchange(CDataExchange* pDX)
    {
    	CDialog::DoDataExchange(pDX);
    	"cpp-comment">//{{AFX_DATA_MAP(CAdvCBDemoDlg)
    		"cpp-comment">// NOTE: the ClassWizard will add DDX and DDV calls here
    	"cpp-comment">//}}AFX_DATA_MAP
    	DDX_Control(pDX, IDC_ADV_COMBO, m_ctlAdvCombo);
    }
    
  7. 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.

  8. 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

    "cpp-keyword">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 = -"cpp-literal">1;
    m_strAdvCombo = "cpp-string">"";
    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

  9. 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.

    Stringtable entry
    Picture 3

Upgrade your current project

This description is not so detailed as the one above. Hopefully you have enough knowledge to manage the following.
  1. 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

  2. Include the file AdvComboBox.h in your header file

  3. Change classnames
    Rename all of your CComboBox variable definitions to CAdvComboBox in your header file.
  4. 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.

  5. 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.
  6. Compile and test
    Your project should now work except the limitations of the CAdvComboBox.

Use CAdvComboBox in a MFC DLL

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.
  1. Declare a CAdvComboBox variable
    Create the class like this:
    CAdvComboBox m_ctlAdvCombo( TRUE )
  2. 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 "cpp-keyword">extern definition in the StdAfx.h file like this:
    "cpp-keyword">extern HINSTANCE g_hDLLInstance;
  3. Initialize the global HINSTANCE
    Set the hDllInstance in the DllMain function.
    g_hDLLInstance = hInstance;
  4. 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;
        "cpp-keyword">if( m_bInst )
        {
            hInst = g_hDLLInstance;
        }
        "cpp-keyword">else
        {
            hInst = AfxGetInstanceHandle();
        }
    
        ASSERT( hInst != "cpp-literal">0 );
    
        ...
    	
        "cpp-keyword">return TRUE;
    }
    
  5. 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:
    "cpp-keyword">class __declspec(dllexport) CAdvComboBox : "cpp-keyword">public CWnd
    DDX_ Definitions:
    _declspec(dllexport) "cpp-keyword">void AFXAPI DDX_ACBIndex( CDataExchange* pDX, 
                                                   "cpp-keyword">int nIDC, "cpp-keyword">int& index );
    _declspec(dllexport) "cpp-keyword">void AFXAPI DDX_ACBString( CDataExchange* pDX, 
                                                    "cpp-keyword">int nIDC, CString& value );

AdvComboBox Functions

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("cpp-keyword">int)
     Definition: "cpp-keyword">int GetDefaultVisibleItems()
     Returns: Number of items.
     
  • GetItemChecked 
     Description: Get the checked status of an item
     Definition: BOOL GetItemChecked( "cpp-keyword">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( "cpp-keyword">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: "cpp-keyword">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: "cpp-keyword">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: "cpp-keyword">void ModifyACBStyle(UINT nRemoveStyle, UINT nAddStyle)
     Parameter: nRemoveStyle
     ACBS style to remove.
     Parameter: nAddStyle
     ACBS style to add.
     Returns: "cpp-keyword">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: "cpp-keyword">void SetDefaultVisibleItems( "cpp-keyword">int nItems = -"cpp-literal">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: "cpp-keyword">void
     
  • SetItemChecked
     Description: Set an item in combo to either check or un-checked.
     Definition: "cpp-keyword">void SetItemChecked( "cpp-keyword">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: "cpp-keyword">void
     
  • SetItemDisabled
     Description: Set an item in combo to either disabled or enabled.
     Definition: "cpp-keyword">void SetItemDisabled("cpp-keyword">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: "cpp-keyword">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: "cpp-keyword">void SetMinVisibleItems("cpp-keyword">int nMinItems)
     Parameter: nMinItems
     Number of minimum listbox items.
     Returns: "cpp-keyword">void
     
  • DDX_ACBIndex
     Description: Use this function to get and set data with MFC's UpdateData(...)
     Definition: "cpp-keyword">void AFXAPI DDX_ACBIndex( CDataExchange* pDX, "cpp-keyword">int nIDC, "cpp-keyword">int& index )
     
  • DDX_ACBString
     Description: Use this function to get and set data with MFC's UpdateData(...)
     Definition: "cpp-keyword">void AFXAPI DDX_ACBString( CDataExchange* pDX, "cpp-keyword">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

AdvComboBox Notifications

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!

AdvComboBox Styles

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

Future Upgrades

  • .NET compatible

Hints & Tips

  • Use ClassWizard
    Create your project with MFC's CComboBox so the ClassWizard can help you with notification implementation like ON_CBN_SELCHANGE. When you have created all events for the combobox, change to CAdvComboBox. This is the easiest and fastest way to implement this class.
  • Set style to flat
    Add a call in OnInitDialog() to ModifyACBStyle(...) like this:
    m_ctlAdvCombo.ModifyACBStyle( "cpp-literal">0, ACBS_FLAT );
  • Windows Theme compatible
    The AdvComboBox is theme compatible, to the extent that I have tested it. Please let me know if any colors or anything else are wrong. Before telling me, compare one more time with a CComboBox please.

Improvements and Bug Fixes

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("cpp-keyword">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("cpp-keyword">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.

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