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

Toolbars with embedded Combo Boxes

0.00/5 (No votes)
7 Aug 2002 1  
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