- Source files of
- Source files of
You should extract the source files to your computer as the same structure as listed above!
Before we start, we may first need to understand the concept of "virtual". A good place to start is virtual list control, if you are already familiar with it, you may skip to the Background part of this article. So let's see what a virtual list control is.
Introduction to Virtual List Control
In Windows, we can create a list control with the style
LVS_OWNERDATA (which means the list control itself does not keep the data but instead it is the owner/parent window that keeps them) and handle the
LVN_GETDISPINFO notification, that's so-called virtual list control.
A virtual list control is useful when we want to show numerous number of items on it, what's more, "adding/deleting" items of a virtual list control is fast (because a virtual list control does not actually keep any data, so it does not need to add or delete something, it is our job to tell it how many items should be shown on it - by calling
SetItemCount(), and it will then ask us back - with the
LVN_GETDISPINFO notification sent to the parent control - for the information to show), you may consider that as a callback.
The Reason of Using Virtual List Control
- According to MSDN, a list control has the default item count only extends to an
int, and sometimes we may want to exceed that limitation.
- The list control must be populated before it starts showing anything, and once we need to add large number of items on it, that will be painfully slow which is unbearable.
- We may want to give some visual feedback dynamically in response to the user input, e.g. filter out some items on the list control while the user types the search field, you can imagine this as a Google Instant. This requires adding and deleting the items frequently, which as we know is also unacceptably slow.
With the help of virtual list control, all the above problems can be solved:
- A virtual list control supports an item count up to a
- A virtual list control does not have any data, we don't even need to populate the items.
- As I have mentioned in the previous part, changing the item count can be done simply by calling
SetItemCount(), we don't need to add or delete anything.
If you want to learn more about virtual list control, you may refer to:
As a demonstration of using virtual list control, here's the snapshot from my other article's demo application:
As you see, a virtual list control is cool, however, what if we want to do the same thing for a combo box? Can we make the list box part within a combo box become virtual? Is that even possible? Sure, that's possible, see the address bar of your web browser and type something.
As we know, the combo box, in fact, is composed of a list box control and (optionally, when it's not
CBS_SIMPLE) an edit control. Like the
LVS_OWNERDATA style for a list control, the list box control can also become virtual if we
create it with the style
LBS_NODATA, see the description on MSDN for it.
Specifies a no-data list box. Specify this style when the count of items in the list box will exceed one thousand. A no-data list box must also have the
LBS_OWNERDRAWFIXED style, but must not have the
How a Virtual List Box Works
First, we to create a class like
CVListBox that derived from
CListBox, to create an instance of it, we need to call
Override the method
CompareItem() like this:
void CVListBox::DrawItem( LPDRAWITEMSTRUCT lpDIS )
if ( (int)lpDIS->itemID < 0 )
CDC* pDC = CDC::FromHandle(lpDIS->hDC);
int nSavedDC = pDC->SaveDC();
CString strText = GetItemText(lpDIS->itemID); CRect rectItem(lpDIS->rcItem);
pDC->DrawText(strText, rectItem, DT_SINGLELINE);
void CVListBox::MeasureItem( LPMEASUREITEMSTRUCT )
int CVListBox::CompareItem( LPCOMPAREITEMSTRUCT )
CString CVListBox::GetItemText( UINT nItem )
The other thing is like a virtual list control, when you want to update the list box, you just simply try this:
SendMessage(myListbox.m_hWnd, LB_SETCOUNT, nCount, 0)
That looks kind of easy, huh? You might as well have thought that we can create the combo box and then try
ModifyStyle() stuff, but unfortunately that's a wrong way to go! As some of us might have experienced, it's not true that every style can be set/removed dynamically, some style like
LVS_OWNERDRAWFIXED/LBS_OWNERDRAWFIXED can only be specified during the creation of the control, the style
LBS_NODATA is also in such case, and we cannot change the style of the list box within a combo box during the creation, so that seems like there is no way to change the style of the list box to make it virtual.
The answer is at first simple: we have to create the combo box ourselves from scratch! This means we need to create the edit control and the
listbox and manage to make them cooperate together, in other words, we need to handle all kind of messages/notification to respond the user's input, that's like reinventing the wheel!
Mathias Tunared had post his/her article AdvComboBox on CodeProject which kind of fit my requirements, however, I found that his/her implementation does not take advantage of virtual list box.
The good news is that Microsoft has provided a sample code called VCOMBOBX for this! That article is published in 2007, then I searched over the web but couldn't find a MFC version of that (maybe I missed), so I decided to do the job.
Using the Code
Using a virtual combo box is just like using a virtual list box, if you are familiar with the virtual list control you will find that easy to use.
Understanding the Class CVComboBox
Basically the class
CVComboBox is the core of this sample, you can locate it in the source file VComboBox.h/cpp.
CVComboBox is a MFC wrapper class that encapsulates a custom combo box control composed with a virtual list box control, the window class name of it is
CVComboBox supports almost all the styles that a normal
CComboBox can have, but with a different macro (although the values of them are the same), listed as follows:
#define VCBS_SIMPLE 0x0001L
#define VCBS_DROPDOWN 0x0002L
#define VCBS_DROPDOWNLIST 0x0003L
#define VCBS_AUTOHSCROLL 0x0010L
#define VCBS_OEMCONVERT 0x0020L
#define VCBS_DISABLENOSCROLL 0x0040L
#define VCBS_UPPERCASE 0x0100L
#define VCBS_LOWERCASE 0x0200L
#define VCBS_DEFAULT (WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_CLIPSIBLINGS | \
WS_CLIPCHILDREN | VCBS_AUTOHSCROLL)
#define VCBS_DEFAULT_SIMPLE (VCBS_DEFAULT | VCBS_SIMPLE)
#define VCBS_DEFAULT_DROPDOWN (VCBS_DEFAULT | VCBS_DROPDOWN)
#define VCBS_DEFAULT_DROPDOWNLIST (VCBS_DEFAULT | VCBS_DROPDOWNLIST)
CVComboBox provides almost all the methods that a
CComboBox have, except that it also provides the following methods in addition:
virtual BOOL Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID);
virtual BOOL CreateFromCtrl
(CWnd* pParent, int nID, DWORD dwStyle = VCBS_DEFAULT_DROPDOWN);
void SetItemCount(int nCount);
void SetDroppedVisibleItemCount(int nCount, BOOL bRepaint = TRUE);
virtual CVComboListBox& GetVComboListBox();
virtual CComboListBox& GetComboListBox();
virtual CVComboEdit& GetComboEdit();
These methods are rather straightforward, I guess you can already understand what they are used for, here I will just give a simple description about the class
CVComboListBox is a class that encapsulate the virtual list box control, it derives from
CComboListBox which derives from
CVComboEdit is just a class that derives from
Since the above classes are encapsulated inside the class
CVComboBox, and in most cases, we don't need to care about them, so now let's see how to use the class
The Basic Steps to Use CVComboBox in a Dialog Based Application
Here are the basic steps to show how to use the class
- Create a dialog based MFC project called
- Add the following source files (under the folder code_base) into your workspace:
- Open the precompiled header file, which is normally StdAfx.h, append this line to it:
- If you are using Visual Studio 6.0, you also need to put these at the beginning of StdAfx.h:
#pragma warning(disable: 4786)
#define WINVER 0x0501
#define _WIN32_WINNT 0x0501
- Declare your own class derived from
CVComboBox and override the necessary methods, normally it would be
GetItemText(), e.g. like this one:
class CMyVComboBox : public CVComboBox
virtual CString GetItemText(UINT nItem)
strText.Format(_T("Item %d"), nItem);
- Open the dialog with resource editor, drag and drop a custom control from the control toolbar, fill the properties as shown below:
- Drag and drop a combo box control onto your dialog, set the id of it to
- Add two member variables to your dialog class like this, and of course you need to put the
#include directive to include necessary header files where you declare the class
- Add the line in bold below to your dialog class's
void CCVComboDemoDlg::DoDataExchange(CDataExchange* pDX)
DDX_Control(pDX, IDC_VCOMBO1, m_vComboBox1);
- Put this code in your
- Compile and run the app!
Points of Interest
At first, I tried to use a
WH_CBT hook to intercept the creation of the list box and then modify its style, however, this solution seems to fail if Common Control version 6 is enabled (when a manifest file is used), that's the reason that I seek for a virtual list box implementation.
As I was writing code for combo box, I found that custom drawing the control is kind of fun, and I wonder how do I implement the fancy looking control in Vista/Win7 and bring them into former OS like WinXP, so I also wrote some other control classes that implemented custom draw.
You may find some interesting/useful control classes in the demo project
CustomControlDemo, as listed here:
These classes have some ability to represent/simulate the fancy Win7 appearance under WinXP and they are VC6/VS2010 compatible.
For some reasons, I have no intention to introduce them in detail or post another article for them, however, you can still explore the source code yourself and you are free to use/modify them in whatever way you like.
Here's a snapshot of the demo application, hope you can have fun playing with it:
- 11/25/2010: Initial release
- 12/29/2010: Improve the drawing code, some minor bugs were fixed