Click here to Skip to main content
15,868,005 members
Articles / Desktop Programming / MFC
Article

Toolbars with embedded Combo Boxes

Rate me:
Please Sign up or sign in to vote.
4.74/5 (23 votes)
7 Aug 20023 min read 220.1K   3.8K   98   38
The article demonstrates how to add single or multiple combo boxes to standard toolbars in MFC applications, how to customize combo boxes behaviour and how to collect user input from them.

Sample Image - ToolbarWithCombo.jpg

Introduction

The Combo box became a standard toolbar feature. Having a combo box is convenient to display a frequently accessed set of choices, or to keep a history of user input (for example, most recent search patterns).

Embedding a standard combo box in a toolbar is fairly straightforward. However, the result will probably not look satisfactory, unless you perform a few more steps. In this article we will show these steps, so you will be able to achieve the following:

  • Add one or more combo boxes to a toolbar;
  • Adjust the combo box font;
  • Make the combo box respond to user commands;
  • Create a history combo box that contains the most recent user entries.

Embedding combo box into a toolbar

To embed a combo box into a toolbar you’ll need to add code to the CMainFrame::OnCreate message handler. To avoid adding low-level toolbar details to the OnCreate method, you can encapsulate them in a dedicated method CreateComboBox that is added to your custom CToolBar-based class:

class CToolBarWithCombo : public CToolBar
{
public:
    CComboBox m_comboBox;

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CToolBarWithCombo)
    //}}AFX_VIRTUAL

    BOOL CreateComboBox(class CComboBox& comboBox, 
                        UINT nIndex, 
                        UINT nID, 
                        int nWidth, 
                        int nDropHeight);

// Generated message map functions
protected:
    //{{AFX_MSG(CToolBarWithCombo)
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};

BOOL CToolBarWithCombo::CreateComboBox(CComboBox& comboBox, 
                                       UINT nIndex, 
                                       UINT nID, 
                                       int nWidth, 
                                       int nDropHeight)
{
    // Create the combo box
    SetButtonInfo(nIndex, nID, TBBS_SEPARATOR, nWidth);

    CRect rect;
    GetItemRect(nIndex, &rect);
    rect.top = 1;
    rect.bottom = rect.top + nDropHeight;
    if (!comboBox.Create(CBS_DROPDOWN|WS_VISIBLE|WS_TABSTOP|WS_VSCROLL,
                         rect, this, nID))
    {
        TRACE("Failed to create combo-box\n");
        return FALSE;
    }

    return TRUE;
}

Then you can call the CreateComboBox method right after your custom toolbar is created (inside CMainFrame::OnCreate):

if (!m_wndToolBar.CreateEx(this, TBSTYLE_FLAT, 
                                WS_CHILD | WS_VISIBLE | CBRS_TOP | CBRS_GRIPPER |
                                CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
	 !m_wndToolBar.LoadToolBar(IDR_MAINFRAME))
{
    TRACE0("Failed to create toolbar\n");
    return -1;      // fail to create
}

// Combo box is assigned index of 3, this means it will be placed after the 
// third button in a toolbar
if (!m_wndToolBar.CreateComboBox(m_wndToolBar.m_comboBox, 3, ID_COMBO, 150, 100))
{
    TRACE0("Failed to create toolbar's combo box\n");
    return -1;      // fail to create
}

The alternative is to override CToolBarWithCombo::OnCreate and place the CreateComboBox call there.

Font adjustment

By default, the combo box is assigned the system font which does not look very good in toolbars. So the next logical step is to make the combo box look better. You can add font selection code directly to CreateComboBox method, but since there are more features we plan to add to our combo boxes, we’ll derive a new class from CComboBox:

class CSmartComboBox : public CComboBox
{
protected:
    CFont m_font;

public:
    CSmartComboBox();

    LONG    m_lfHeight;
    LONG    m_lfWeight;
    CString m_strFaceName;

    BOOL CreateFont(LONG lfHeight, LONG lfWeight, LPCTSTR lpszFaceName);

// Generated message map functions
protected:
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    DECLARE_MESSAGE_MAP()
};

CSmartComboBox::CSmartComboBox()
{
    m_lfHeight = -10;
    m_lfWeight = FW_NORMAL;
    m_strFaceName = _T("MS Sans Serif");
    m_nMaxStrings = 10;
}

BEGIN_MESSAGE_MAP(CSmartComboBox, CComboBox)
    ON_WM_CREATE()
END_MESSAGE_MAP()

int CSmartComboBox::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CComboBox::OnCreate(lpCreateStruct) == -1)
        return -1;

    if( !CreateFont(m_lfHeight, m_lfWeight, m_strFaceName) )
    {
        TRACE0("Failed to create font for combo box\n");
	return -1;      // fail to create
    }

    return 0;
}

BOOL CSmartComboBox::CreateFont(LONG lfHeight, LONG lfWeight, LPCTSTR lpszFaceName)
{
    //  Create a font for the combobox
    LOGFONT logFont;
    memset(&logFont, 0, sizeof(logFont));

    if (!::GetSystemMetrics(SM_DBCSENABLED))
    {
        // Since design guide says toolbars are fixed height so is the font.
        logFont.lfHeight = lfHeight;
        logFont.lfWeight = lfWeight;
        CString strDefaultFont = lpszFaceName;
        lstrcpy(logFont.lfFaceName, strDefaultFont);
        if (!m_font.CreateFontIndirect(&logFont))
        {
            TRACE("Could Not create font for combo\n");
            return FALSE;
        }
        SetFont(&m_font);
    }
    else
    {
        m_font.Attach(::GetStockObject(SYSTEM_FONT));
        SetFont(&m_font);
    }
    return TRUE;
}

Now the combo box can easily be adjusted to reflect the style of your application.

Adding response to user commands

Once combo box is embedded into a toolbar, its properties can be accessed in a traditional manner. But in order to provide prompt response to user commands, embedded combo boxes need to cooperate with their container, i.e. the toolbar. Then, the user will be able to type some text into a combo box, press "Enter", and the corresponding command is sent to an application.

We will demonstrate this step with an application that has two combo boxes embedded in a toolbar. This is important to verify that the command is fetched by the right combo box.

class CToolBarWithCombo : public CToolBar
{
public:

    CSmartComboBox	m_comboBox1;
    CSmartComboBox	m_comboBox2;

// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CToolBarWithCombo)
    virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);
    //}}AFX_VIRTUAL

    // The rest of the code is skipped
}

BOOL CToolBarWithCombo::OnCommand(WPARAM wParam, LPARAM lParam) 
{
    if( wParam == IDOK && lParam == 0 )
    {
        CString strText;
	CString strCommand;
	CComboBox* rgComboBox[] = {&m_comboBox1, &m_comboBox2};
	for( int index = 0; 
             index < sizeof(rgComboBox) / sizeof(rgComboBox[0]); 
             ++index )
        {
            if( rgComboBox[index]->GetEditSel() != 0 )
            {
                rgComboBox[index]->GetWindowText(strText);
                strCommand.Format(_T("Command from ComboBox[%d]: %s"), 
                                  index+1, 
                                  (LPCTSTR)strText);
                AfxMessageBox(strCommand);
		rgComboBox[index]->AddString(strText);
		rgComboBox[index]->SetWindowText(_T(""));
            }
        }
    }
	
    return CToolBar::OnCommand(wParam, lParam);
}

As you can see, the trick to find which combo box has focus is to call the GetEditSel function. Unless the cursor is placed in a combo box, the function will return 0.

Managing combo box entries

The last of our improvements is to create a combo box that will manage recent user entries. Such combo boxes are typically populated with the history of most recent search patterns, dialed phone numbers etc. They have the following common features:

  • Maximum number of items is limited, once this number is reached, new entries replace oldest entries;
  • Entries are added to the top of the list;
  • List does not contain duplicates.

Here is the code you’ll need to add to CSmartComboBox:

class CSmartComboBox : public CComboBox
{
    // Only new code is shown
public:

    int m_nMaxStrings;

    int	AddString(LPCTSTR str);
    int	InsertString(int index, LPCTSTR str);
};

int CSmartComboBox::AddString(LPCTSTR str)
{
    if( _tcslen(str) == 0 )
        return -1;

    int oldIndex = FindStringExact(-1, str);
    if( oldIndex >= 0 )
        DeleteString(oldIndex);

    if( GetCount() == m_nMaxStrings )
        DeleteString(m_nMaxStrings-1);

    return CComboBox::InsertString(0, str);
}

int CSmartComboBox::InsertString(int index, LPCTSTR str)
{
    if( _tcslen(str) == 0 )
        return -1;

    int oldIndex = FindStringExact(-1, str);
    if( oldIndex >= 0 )
    {
        DeleteString(oldIndex);
	if( index >= oldIndex )
	    --index;
    }

    if( GetCount() == m_nMaxStrings )
        DeleteString(m_nMaxStrings-1);

    return CComboBox::InsertString(index, str);
}

The sample project that follows this article contains source code and a test application that demonstrates embedded combo boxes.

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
Architect Miles AS
Norway Norway
Vagif Abilov is a Russian/Norwegian software developer and architect working for Miles. He has more than twenty years of programming experience that includes various programming languages, currently using mostly C# and F#.

Vagif writes articles and speaks at user group sessions and conferences. He is a contributor and maintainer of several open source projects, including Simple.Data OData adapter, Simple.OData.Client and MongOData.

Comments and Discussions

 
GeneralMy vote of 5 Pin
cocobanana22-Jun-12 6:15
cocobanana22-Jun-12 6:15 
GeneralMy vote of 5 Pin
xhk27-Sep-10 21:31
xhk27-Sep-10 21:31 
GeneralEscape and arrow key Pin
OliParent23-May-06 7:21
OliParent23-May-06 7:21 
Generalthx for example Pin
drobus10-Apr-06 9:56
drobus10-Apr-06 9:56 
GeneralVery interesting! How to move the combobox later Pin
GSL317-Jan-06 12:04
GSL317-Jan-06 12:04 
GeneralFlat-Style Toolbar Separator Line Pin
Christian Cheney7-Jan-05 9:26
Christian Cheney7-Jan-05 9:26 
GeneralRe: Flat-Style Toolbar Separator Line Pin
Robert Marshall2-May-05 6:16
Robert Marshall2-May-05 6:16 
GeneralMy control is putted at the wrong place Pin
Filomela12-May-04 20:26
Filomela12-May-04 20:26 
QuestionCDateTimeCtrl in ToolBar ? Pin
BrutalDeath026-Jan-04 5:51
sussBrutalDeath026-Jan-04 5:51 
AnswerRe: CDateTimeCtrl in ToolBar ? Pin
BrutalDeath026-Jan-04 6:14
sussBrutalDeath026-Jan-04 6:14 
Generalwhen shown in dialog this doesn't work Pin
verinder_bindra10-Jan-04 17:50
verinder_bindra10-Jan-04 17:50 
problem is seperator control is shown in the middle of combo box instead of at the side.. We are using Seperator Ctrl for showing the Combo but changing the width of seperator Moves the seperator In the mid of width and Control is Shown in left side so that is why it appears at the Side..

verinder

none
QuestionWhat a stupid article Pin
Barretto VN20-Dec-03 23:08
Barretto VN20-Dec-03 23:08 
AnswerRe: What a stupid article Pin
Vagif Abilov21-Dec-03 9:33
professionalVagif Abilov21-Dec-03 9:33 
GeneralRe: What a stupid article Pin
Barretto VN3-Jan-04 0:51
Barretto VN3-Jan-04 0:51 
GeneralRe: What a stupid article Pin
Vagif Abilov3-Jan-04 2:13
professionalVagif Abilov3-Jan-04 2:13 
GeneralRe: What a stupid article another Reply Pin
Barretto VN3-Jan-04 0:52
Barretto VN3-Jan-04 0:52 
GeneralRe: What a stupid article another Reply Pin
Anna-Jayne Metcalfe21-Apr-04 7:05
Anna-Jayne Metcalfe21-Apr-04 7:05 
GeneralUse in project for school eventually commercial Pin
Martin Meister2-Oct-03 8:02
Martin Meister2-Oct-03 8:02 
GeneralRe: Use in project for school eventually commercial Pin
Vagif Abilov2-Oct-03 20:11
professionalVagif Abilov2-Oct-03 20:11 
GeneralIf you intend to call CreateComboBox in CToolBarWithCombo::OnCreate... Pin
Brendan Tregear6-Aug-03 15:17
Brendan Tregear6-Aug-03 15:17 
GeneralRe: If you intend to call CreateComboBox in CToolBarWithCombo::OnCreate... Pin
flyingxu20-Mar-08 1:34
flyingxu20-Mar-08 1:34 
GeneralA simple font controlling Pin
Ken Keray4-Aug-03 3:53
Ken Keray4-Aug-03 3:53 
GeneralCombo box unresponsive when toolbar added to rebar band Pin
Tweaker1-Nov-02 8:33
Tweaker1-Nov-02 8:33 
GeneralCan't enter lower-case 'a' in combo box! Pin
lotus_boy25-Oct-02 10:46
lotus_boy25-Oct-02 10:46 
GeneralRe: Can't enter lower-case 'a' in combo box! Pin
Mel Feik25-Oct-02 11:30
Mel Feik25-Oct-02 11:30 

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.