![]() |
Desktop Development »
Combo & List Boxes »
ComboBox Controls
Intermediate
License: The Code Project Open License (CPOL)
A separator combo-boxBy Zuoliu DingA CComboBox derived combo-box class |
VC7, VC7.1, Windows, MFC, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
Recently I met a project requirement based on the current code to enhance a combo box with some separators, setting its items apart from different meanings, such as an example shown as below:

By searching websites, I saw many guys also looking for such an adapted combo box control, while I was not able to find an ideal match to my use exactly. In Code Project, the article A Custom Group Combo Box created by Brett R. Mitchell looks good, as it groups its items with distinguishing lines and headers. To fit my use, I might make a header empty and leave a full width line as a separator.
However, this custom combo box called CDropButton uses a CButton
class as the main class, and contains a CWnd and a CListbox
for data display, which is not a class derived from the standard CCombobox.
Thus there is no way to combine it into our current code base as a genuine
combo box, since we use CCombobox methods and events frequently.
Also as no edit box in that custom control, it loses the editable feature.
Therefore, I decided to write a CCombobox derived class called CSeparatorComboBox
to fit my requirement. By using it, I only need to change the class type in our
current code base to maintain its functionality without touch others, and then
add two separators with a new method like this,
m_ctrlCombo.SetSeparator(0); m_ctrlCombo.SetSeparator(-1);
Where m_ctrlCombo is an object of CSeparatorComboBox and
SetSeparator(0) adds a separator after the first item indexed by
zero as after �All Fruits� in the figure. SetSeparator(-1) is for
the second separator positioned one item before last. Its alternative can be SetSeparator(5).
Now any CCombobox event is available. When selecting the item
�Apple�, I can receive ON_CBN_SELCHANGE and show the result here
in my test program:

Editing it to �Apple-2� is responded by ON_CBN_EDITCHANGE with the
result below:

The main point for the separator combo box design is that the separator should
not be selected either from UI or in the program logic. Generally, we may have
two choices: First, let a separator occupy some space similar to an item but no
actual selection to it. This is usually implemented in the owner draw style to
manually draw both text and separator, just as what CDropButton did.
The second way is simply drawing a line between items in the dropdown list, with the same height for each item and no extra space for the separator. The owner draw in this case is not necessary. For my problem, this seems easier and enough.
To draw a line between items without the owner draw style, I have to decide
which message handler is appropriate, and how to get a handle to the dropdown
list and its device-context as well. Recommended by Microsoft KB article
Q174667, I decide to intercept the WM_CTLCOLOR message to subclass
CListBox inside CComboBox in my CSeparatorComboBox
and do the drawing as follows:
HBRUSH CSeparatorComboBox::OnCtlColor(CDC* pDC,
CWnd* pWnd, UINT nCtlColor)
{
if (nCtlColor == CTLCOLOR_LISTBOX)
{
if (m_listbox.GetSafeHwnd() ==NULL)
{
m_listbox.SubclassWindow(pWnd->GetSafeHwnd());
}
CRect r;
int nIndex, n = m_listbox.GetCount();
CPen pen(m_nPenStyle, m_nSepWidth, m_crColor), *pOldPen;
pOldPen = pDC->SelectObject(&pen);
for (int i=0; i< m_arySeparators.GetSize(); i++)
{
nIndex = m_arySeparators[i];
if (nIndex<0) nIndex += n-1;
if (nIndex < n-1)
{
m_listbox.GetItemRect(nIndex, &r);
pDC->MoveTo(r.left+m_nHorizontalMargin,
r.bottom-m_nBottomMargin);
pDC->LineTo(r.right-m_nHorizontalMargin,
r.bottom-m_nBottomMargin);
}
}
pDC->SelectObject(pOldPen);
}
return CComboBox::OnCtlColor(pDC, pWnd, nCtlColor);
}
Note that for subclassing to occur, the dialog box must be painted at least
once. At the first time, by calling SubclassWindow(), I construct
the m_listbox object alive and then call its GetItemRect()
to retrieve an item coordinate specified by m_arySeparators, which
is an integer array containing all the separator positions filled by the SetSeparator()
function as mentioned earlier. Using pDC to draw a line is
so simple. And in cleanup, don�t forget to call UnsubclassWindow() like this,his,
void CSeparatorComboBox::OnDestroy() { if (m_listbox.GetSafeHwnd() !=NULL) m_listbox.UnsubclassWindow(); CComboBox::OnDestroy(); }
The following is the CSeparatorComboBox class.
class CSeparatorComboBox : public CComboBox { DECLARE_DYNAMIC(CSeparatorComboBox) CListBox m_listbox; CArray<int> m_arySeparators; int m_nHorizontalMargin; int m_nBottomMargin; int m_nSepWidth; int m_nPenStyle; COLORREF m_crColor; public: CSeparatorComboBox(); virtual ~CSeparatorComboBox(); void SetSeparator(int iSep); void AdjustItemHeight(int nInc=3); void SetSepLineStyle(int iSep) { m_nPenStyle = iSep; } void SetSepLineColor(COLORREF crColor) { m_crColor = crColor; } void SetSepLineWidth(int iWidth) { m_nSepWidth = iWidth; } void SetBottomMargin(int iMargin) { m_nBottomMargin = iMargin; } void SetHorizontalMargin(int iMargin) { m_nHorizontalMargin = iMargin; } protected: DECLARE_MESSAGE_MAP() public: afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); afx_msg void OnDestroy(); };
To consider visual effect on a separator between items, I provide a function to
adjust the item height as follows. It increases an item height based on the
current item�s relative height, the default parameter nInc is
three pixels.
void CSeparatorComboBox::AdjustItemHeight(int nInc/*=3*/) { SetItemHeight(0, nInc+ GetItemHeight(0)); }
When the first separator is set, SetSeparator() automatically
adjusts the default item height for you. But you can call AdjustItemHeight() to overwrite the default. If no separator set, no change the item height.
void CSeparatorComboBox::SetSeparator(int iSep) { if (!m_arySeparators.GetSize()) AdjustItemHeight(); m_arySeparators.Add(iSep); }
The rest five functions are used to set the separator attributes:
SetSepLineStyle() sets a separator�s line style (PS_SOLID=0,
PS_DASH=1, PS_DOT=2, etc.), default to dot line.
SetSepLineColor()
sets a separator�s color, default to dark gray.
SetSepLineWidth() sets a separator�s line width, default to 1 pixel. SetBottomMargin() sets a separator�s bottom margin, default to 2 pixels. SetHorizontalMargin() sets a separator�s horizontal margin,
default to 2 pixels.To use CSeparatorComboBox, just copy two source files SepComboBox.h and SepComboBox.cpp to your project and include SepComboBox.h wherever you
want. Define a variable like
CSeparatorComboBox m_ctrlCombo;
In an initialization procedure, add text strings using CComboBox methods:
m_ctrlCombo.AddString("All Fruits"); m_ctrlCombo.AddString("Banana"); m_ctrlCombo.AddString("Orange"); m_ctrlCombo.AddString("Apple"); m_ctrlCombo.AddString("Pear"); m_ctrlCombo.AddString("Watermelon"); m_ctrlCombo.AddString("*Add/Edit Fruit");
Next, set separator positions like this
m_ctrlCombo.SetSeparator(0); m_ctrlCombo.SetSeparator(-1);
Optionally, set any attributes you want, such as:
m_ctrlCombo.SetSepLineStyle(PS_SOLID); m_ctrlCombo.SetSepLineColor(0); m_ctrlCombo.SetHorizontalMargin(1);
And set a current selection with SetCurSel() and use any CComboBox
methods and event handlers. That�s all - what you see at the beginning of this
article. For more details, please see CSepComboTestDlg in my demo
package. Although the demo is programmed in VC7, it should work the same in the
previous Visual C++, as long as MFC is available.
Recall another way to implement a separator combo box I mentioned with the owner
draw style, right? If you want a better UI visually, may try that idea � no
change the current text item height but make room for a separator similar to an
item. Override the virtual functionor <code>MeasureItem() to manually set positions, draw texts and separators. This might need a bit
more work, while a better separator combo box is for sure and worthwhile..
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 13 Jun 2004 Editor: Nishant Sivakumar |
Copyright 2004 by Zuoliu Ding Everything else Copyright © CodeProject, 1999-2009 Web21 | Advertise on the Code Project |