Click here to Skip to main content
15,890,995 members
Articles / Desktop Programming / MFC
Article

A Custom Group Combo Box

Rate me:
Please Sign up or sign in to vote.
4.80/5 (17 votes)
12 Jun 20013 min read 211K   3.7K   82   23
A combobox-like control that allows groupings within the elements in the dropdown list.

Introduction

During the development of a larger application I found myself needing a CCombobox type control that could have groupings inside of it. I wanted the control to resemble more of a button apparatus but with a drop down that a user could select from...hence the CDropButton class was created. You may have seen this type of control used before in Microsoft Money. It's an excellent way to group items in a drop list.

The control is quite functional using a CButton class for the main class, and a CWnd and a CListbox for displaying the data. It is completely customizable, with the ability to change colors of just about everything except the scrollbar, which may come later.

It works like a regular CCombobox except the items are grouped under headers. Headers appear in bold and a distinguishing line. Headers themselves are unselectable and their data is alphabetical. The CDropButton can be set to either replace the text on the button with a selection or not.

Here's what the default control looks like:

Here's what a custom control could look like (pretty huh?):

The control is very easy to use:

Create a default CDropButton using the following create:

m_pDropBtn = new CDropButton();
m_pDropBtn->Create("Options", CRect(100,100,280,120), this, ID_DROP_BUTTON);

You can also send in lots of options in the create in order to customize to your preference!

COLORREF crButtonBackgroundColor = RGB(150,0,0);
COLORREF crButtonBackgroundOverColor = RGB(200,50,50);
COLORREF crButtonTxtColor = RGB(255,255,255);
COLORREF crButtonTxtOverColor = RGB(255,255,150);
CFont* pTextFont = NULL;

m_pDropBtn->Create("Options", CRect(300,100,450,120), this, ID_DROP_BUTTON,
                    crButtonBackgroundColor, crButtonBackgroundOverColor,
                    crButtonTxtColor, crButtonTxtOverColor, pTextFont);

// Set OPTIONAL parameters
m_pDropBtn->SetListHeight(200);    // Set the Width Height
m_pDropBtn->SetListWidth(170);    // Set the List Width
m_pDropBtn->ChangeTitleWithSelection(
  false);    // When the users selects an item change the title in the top

In the example above I do not set a font but this is possible, it defaults to 14 point arial. The 'crButtonBackgroundOverColor' parameter makes the button change color when the mouse passes over the control.

After the CDropButton is created you can then get a hold of the list box and set optional properties.

// Get the CListBox
CDropList* pList = m_pDropBtn->GetList();

// Set OPTIONAL paramters
pList->SetBkColor(crBkListColor);          // List background COlor
pList->SetHighlightBkColor(
  RGB(255,255,100));     // List highlight background color

Then add the data. Items in the first group do not have a header. You use the standard CListBox AddString function to add them.

// Add default Items
pList->AddString("Split/Multiple Catagories...");
pList->AddString("Add New Category...");

You can add specific items to a group. You specify the group as the second parameter. The third and forth parameters are optional text colors for that specific item. Every item can have a specific color!

// Add specific items with COLOR!!
pList->AddString("EXPENSE", 2, RGB(128,0,0), RGB(255,0,0));
pList->AddString("Automobile", 2);
pList->AddString("Health Care", 2);
pList->AddString("Gifts", 2);

pList->AddString("INCOME", 3, RGB(0,128,0), RGB(0,255,0));
pList->AddString("Wages/Salary", 3);
pList->AddString("Retirement Income", 3);
pList->AddString("Other Income", 3);

pList->AddString("SPECIAL", 4, RGB(0,0,128), RGB(0,0,255));
pList->AddString("Transfer", 4);
pList->AddString("Loan Payment", 4);
pList->AddString("Clothing", 2); // Out of order but OK!

The control sends back only one message. It sends you a CM_SELECTION_CHANGED message to let you know when a selection has changed. To implement it, place the following statement in your message map

ON_MESSAGE(CM_SELECTION_CHANGED, OnSelectionChanged)

Place this line in your header file:

afx_msg LONG OnSelectionChanged(UINT wParam, LONG lParam);

The function to handle this would look something close to this:

LONG CTestView::OnSelectionChanged(UINT wParam, LONG lParam)
{
    // What control sent the message
    CDropList* pList;
    switch(wParam)
    {
        case    ID_DROP_BUTTON:
                pList = m_pDropBtn->GetList();
                break;

        case    ID_DROP_BUTTON2:
                pList = m_pDropBtn2->GetList();
                break;
    }
    
    // Get the Current Selection and Display it
    CString csText;
    pList->GetText(pList->GetCurSel(), csText);
    AfxMessageBox(csText);

    return 1;
}
The wParam is the ID of the Control that sent the message.

Most all the standard CListBox functions will work except a few. I have NOT tested this for a multiple selection CListBox! In my case, I did not need this therefore did not try to account for it. Also InsertString will not work, the control handles the position of the data items.

RemoveString does work but a little differently when deleting a header item. When a header item is removed so are all of its children.

Most all of the other standard functions should work (SelectString, SetCurSel, etc.) but be aware I did not test ALL of these functions. Be sure to try them out first!

One last thing, horizontal scrolling does not work! I think that it is unnecessary for this control. If you really need it however, simply add the style to the CDropList.

The code behind CDropButton should be very easy to follow and easy to modify. If you make changes to the code (like a custom scrollbar) please send me a copy! I'd love to see it!

This was created on Windows 2000 using Visual C++ 6.0.

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


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionnon unicode and selection from code Pin
klingacik2-Feb-24 1:36
klingacik2-Feb-24 1:36 
Questionhow to set keyboard focus, Pin
emmmatty122-Oct-10 22:04
emmmatty122-Oct-10 22:04 
QuestionCan you help me Pin
sebilkullan16-Dec-09 22:20
sebilkullan16-Dec-09 22:20 
GeneralGood one. Pin
sambathr15-May-09 3:09
sambathr15-May-09 3:09 
GeneralGreat idea Pin
Red Dy2-Nov-06 10:27
Red Dy2-Nov-06 10:27 
QuestionHow to convert this in to c# Pin
Zapss14-Feb-06 23:34
Zapss14-Feb-06 23:34 
Generalkin not working on ppc Pin
amnwit24-Dec-05 10:52
amnwit24-Dec-05 10:52 
Questionhow to write the same code in C# Pin
Zapss23-Dec-05 19:28
Zapss23-Dec-05 19:28 
GeneralA Custom Group Combo Box Pin
guya22-Aug-04 16:51
guya22-Aug-04 16:51 
GeneralPossible improvement Pin
Albert Hermann21-Jun-04 11:04
Albert Hermann21-Jun-04 11:04 
I like this class and use it in one of my actual projects. But i found it a little to complicate to get allways the positioning right, and didn´t like to have to adapt the code everytime when i had to move around my dialog elements. So i wished that i could use a not to be seen rectangular control (like an editbox, combobox etc) to position the dropbutton on my dialogs. And it was actually not that hard to add this functionalty:

Just add this to the 'DropBtn.h' header file, just below the original
BOOL Create(CString csCaption, CRect Rect, CWnd* pParent, UINT uID, .........

BOOL CreateFromDlgItem(CString csCaption,
            UINT uBaseID,
            CWnd* pParent,
            UINT uID,
            COLORREF crBkColor = -1,
            COLORREF crBkOverColor = -1,
            COLORREF crTxtColor = -1,
            COLORREF crTxtOverColor = -1,
            CFont* pTextFont = NULL,
            COLORREF crBkListColor = RGB(255,255,255));

And put the following code at the end of the 'DropBtn.cpp' source file:
//Use a control as base for position/size for creation
BOOL CDropButton::CreateFromDlgItem(CString csCaption, 
                                    UINT uBaseID, 
                                    CWnd* pParent, 
                                    UINT uID, 
                                    COLORREF crBkColor, 
                                    COLORREF crBkOverColor, 
                                    COLORREF crTxtColor, 
                                    COLORREF crTxtOverColor, 
                                    CFont* pTextFont, 
                                    COLORREF crBkListColor)
{
 CWnd *pItem;
 CRect rRect;

 ASSERT(uBaseID!=uID);                //Template ID must no be equal ctrl ID!!
 ASSERT(::IsWindow(pParent->m_hWnd)); //Parent is not a window!!
 pItem=pParent->GetDlgItem(uBaseID);
 ASSERT( pItem != NULL );             //Template control is not part of parent window!!
 if (pItem!=NULL)
 { 
   pItem->ShowWindow(SW_HIDE);        //make our template control invisible and disabled
   pItem->EnableWindow(FALSE);
   pItem->GetWindowRect( &rRect );    //get the template control's rectangle
   pParent->ScreenToClient( &rRect );
   return (Create(csCaption, rRect, pParent, uID, crBkColor, 
                  crBkOverColor, crTxtColor, crTxtOverColor, 
                  pTextFont,  crBkListColor));
 }
 return FALSE;
}

How to use this:

In the dialog wizard, draw at your dialog an editbox (can be also combo/list etc) with the dimensions you wish the dropbutton to have at the place where to be put. Give this control an ID you desire (remember that ID), but do NOT add any variable or callback for that control, just draw it and identify it (no DDX, etc).

Call the new create funtion myBT.CreateFromDlgItem(....) INSTEAD of the original myBT.Create(....) one, just that you now pass in the second parameter the CtrlID of the control you have drawn as template, instead of the position RECT. Just parameter 2 has changed, everything else is exact as original function.

The control used for positioning will be totally overwritten by the DropButton and so cannot be used for nothing more! It just is used as a template for getting the DropButton more conveniently on the dialog.

Actually, i have made alot more additions to the DropBtn (mine now looks exactly like a MFC combobox, handles correctly disable state etc...), but those modifications may not be interessing enough and cannot be added so simply, i would have to post the wole code Sigh | :sigh:

Anyway, again huge thanks to Brett Mitchell for this very nice class.

Albert
GeneralDemo Pin
songleo23-Jan-04 9:01
songleo23-Jan-04 9:01 
GeneralFlicker problem Pin
Albert Hermann19-Dec-03 6:46
Albert Hermann19-Dec-03 6:46 
Generala small bug Pin
Jack Chen17-Jun-02 6:39
Jack Chen17-Jun-02 6:39 
GeneralPretty awful interface, please never give this to real users Pin
9-May-02 20:32
suss9-May-02 20:32 
GeneralRe: Pretty awful interface, please never give this to real users Pin
David Wulff28-May-02 15:20
David Wulff28-May-02 15:20 
GeneralRe: Pretty awful interface, please never give this to real users Pin
Member 6792919-Feb-04 3:18
Member 6792919-Feb-04 3:18 
GeneralRe: Pretty awful interface, please never give this to real users Pin
Haywire Guy10-Jan-06 19:06
Haywire Guy10-Jan-06 19:06 
GeneralMissing demo project Pin
9-Nov-01 5:00
suss9-Nov-01 5:00 
GeneralStrange behavior of Drop Button Pin
Trinh Minh Cuong25-Aug-01 18:29
Trinh Minh Cuong25-Aug-01 18:29 
GeneralRe: Strange behavior of Drop Button Pin
Juvenal Guzman5-Sep-01 5:43
Juvenal Guzman5-Sep-01 5:43 
GeneralA demo application is welcome! Pin
Davide Calabro13-Jun-01 3:05
Davide Calabro13-Jun-01 3:05 
GeneralRe: A demo application is welcome! Pin
17-Jul-01 14:15
suss17-Jul-01 14:15 
GeneralRe: A demo application is welcome! Pin
24-Aug-01 2:09
suss24-Aug-01 2:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.