MultiColumnComboBoxEx: An Extended Data-Bound Multiple Column ComboBox
A customized multi-column ComboBox supports separate box and item height

Introduction
There are already some multiple column ComboBox
controls on The Code Project, such as A data-bound multi-column combobox (Nishant Sivakumar, updated Jul 2007) and Searchable MultiColumn ComboBox with Linked TextBox (Darryl Caillouet, updated Jan 2008). These two customized controls named as MultiColumnComboBox
have one bug and some shortcomings:
- One bug: Must click two times when
Items
is empty andDrawMode
isOwnerDrawVariable
. The reason may be that the first click will drop down but no dropdown box is shown, and the second click closes the invisible dropdown box! Obviously, they behave differently from normalComboBox
which shows one empty dropdown box whenItems
is empty. - Shortcoming one: Cannot set box height and item height separately, because the
ItemHeight
property denotes both height values. - Shortcoming two: Cannot show multiple columns text in box, they display only multiple columns in dropdown box.
- Shortcoming three: Does not provide methods similar to
Items.IndexOf
to find item index whenDataSource
has multiple columns.
The customized ComboBox
control MultiColumnComboBoxEx
introduced here fixes the bug and has the main features below:
- Can show multiple columns not only in dropdown box but also in text box
- Can set box height and item height separately
- Can assign displayed column names and their orders
- Uses
ItemIndexOf
instead ofItems.IndexOf
to find item index whetherDataSource
isnull
or not - Supports
RightToLeft(RTL)
style
MultiColumnComboBoxEx Control
1) New Properties
MultiColumnComboBoxEx
control has some new public
properties:
ColumnPadding
: The padding between columns. Default value is 5.ComboBoxHeight
: The height of box itself. Default value is 12.DisplayColumnNames
: Thestring
of column names separated by comma(,) or |, which are not case sensitive. For example, "employee id, Name
" will show Employee Id and Name columns of the boundDataSource
. Default value is empty, which will show all columns.DisplayMultiColumnsInBox
: If it istrue
andDropDownStyle
isDropDownList
, it will show multiple columns text in box without vertical line. Default value isfalse
.DisplayVerticalLine
: If it istrue
, it will show vertical separate line in dropdown box. Default value istrue
.ItemDropDownHeight
: The height of item in dropdown box. Default value is 12.TextDisplayed
: The displayed text separated by comma(,) when multiple columns are shown. Default value is empty.
2) Disabled and Overridden Properties
Three public
properties of ComboBox
are disabled or invisible in Visual Studio Property Editor: ItemHeight
, IntegralHeight
and DrawMode
. ItemHeight
will denote the height of box itself and is replaced by property ComboBoxHeight
, DrawMode
is OwnerDrawFixed
and IntegralHeight
is true
in constructor function of MultiColumnComboBoxEx
, and their values will be changed according to Items.Count
is zero or not.
Two public
properties are overridden: DropDownStyle
and MaxDropDownItems
. DropDownStyle
cannot to be Simple
, and MaxdropDownItems
will recompute the DropDownHeight
while its max value changes.
3) Public Methods
MultiColumnComboBoxEx
control provides four overload public
methods ItemIndexOf
:
-
public int ItemIndexOf(string itemValue, bool ignoreCase, string columnName)
-
public int ItemIndexOf(string itemValue, string columnName)
-
public int ItemIndexOf(string itemValue, bool ignoreCase)
-
public int ItemIndexOf(string itemValue)
If the DataSource
is null
, ItemIndexOf
is equal to Items.IndexOf
, otherwise it returns the item index of assigned column whose value equals to itemValue
, and returns -1
when no corresponding item exists.The parameters are defined below:
itemValue
: The item value to findignoreCase
: If it istrue
, thestring
compare will not be case sensitive. DefaultignoreCase
value istrue
.columnName
: The column name whose value will be found. Default column isDisplayMember
which is the property ofComboBox
.
Key Points
In order to set the height of the box and item separately and to fix the bug introduced before, the key point is DrawMode
property of ComboBox
, whose value will be in two different states:
DrawMode
must beOwnerDrawVariable
if we want to change the height of box and item separately.DrawMode
must beOwnerDrawFixed
if we want to show empty dropdown box whenItems
is empty andDrowDownStyle
isDropDownList
.
Another key property is IntegralHeight
. ComboBox
shows empty dropdown box if IntegralHeight = true
and DrawMode = OwnerDrawFixed
when Items
is empty.
1) Set Height of Box and Item Separately
ComboBox
has property ItemHeight
to set box and item height when DrawMode
is OwnerDrawFixed
or OwnerDrawVariable
, but both height values will be changed at the same time, of course both are same values. In MultiColumnComboBoxEx
, we use ItemHeight
property to denote the height of box itself, and use OnMeasureItem
event method to get item height which is the value of the member field m_itemDropDownHeight
:
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
// ... other code
e.ItemHeight = m_itemDropDownHeight;
}
We must point out firstly that OnMeasureItem
event method can only be raised when DrawMode
is OwnerDrawVariable
. Secondly, ComboBox
method RefreshItems
can raise the MeasureItem
event and call OnMeasureItem
method.
2) Draw Multiple Columns in Box
When DropDownStyle
is DropDownList
and DisplayMultiColumnsInBox
is true
, MultiColumnComboBoxEx
will show multiple columns in box of ComboBox
. The skill is to test DrawItemEventArgs e.State
in OnDrawItem
method whether it has DrawItemState.ComboBoxEdit
bit, i.e., it is drawing box text if (e.State & DrawItemState.ComboBoxEdit) != 0
.
3) Handle the Case when Items is Empty
When Items
is empty, such as no items or DataSource
is null
, we want to see an empty dropdown box while we click ComboBox
as normal Combobox
behaves (obviously, it denotes no items), but it does so only if DrawMode
is Normal
or OwnerDrawFixed
and IntegralHeight
is true
. If DrawMode
is OwnerDrawVariable
or IntegralHeight
is false
, we must click two times to leave the ComboBox
object, which we can test with the code introduced above.
Further more, we must handle the cases Items.Count
is 0
or greater than 0
. Since ComboBox
has no item added/deleted events, we can only capture the item add message(WM_ADDITEM = 0x0143)
and item delete message(WM_DELETEITEM = 0x0144
) by overriding method WndProc
:
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_LBUTTONDOWN || m.Msg == WM_LBUTTONDOUBLECLICK)
{
if (this.Items.Count == 0) // Items is empty.
{
if (base.DrawMode != DrawMode.OwnerDrawFixed)
{
base.DrawMode = DrawMode.OwnerDrawFixed;
base.DropDownHeight = m_minDropDownHeight;
base.IntegralHeight = true;
base.DroppedDown = true; // drop down once more.
}
else if (base.DropDownHeight != m_minDropDownHeight)
{
base.DropDownHeight = m_minDropDownHeight;
base.IntegralHeight = true;
base.DroppedDown = true;
}
else if (base.IntegralHeight == false)
{
base.IntegralHeight = true;
base.DroppedDown = true;
}
else
{
base.WndProc(ref m);
}
}
else // Items is not empty.
{
if (base.DrawMode != DrawMode.OwnerDrawVariable)
{
base.DrawMode = DrawMode.OwnerDrawVariable;
base.IntegralHeight = false;
base.DroppedDown = true;
}
else if (base.DropDownHeight != m_maxDropDownHeight)
{
base.DropDownHeight = m_maxDropDownHeight;
base.IntegralHeight = false;
base.DroppedDown = true;
}
else if (base.IntegralHeight == true)
{
base.IntegralHeight = false;
base.DroppedDown = true;
}
else
{
base.WndProc(ref m);
}
}
}
else if (m.Msg == WM_ADDITEM && base.Items.Count == 1) // add one item already.
{
if (base.DropDownHeight != m_maxDropDownHeight)
{
base.DropDownHeight = m_maxDropDownHeight;
}
base.WndProc(ref m);
}
else if (m.Msg == WM_DELETEITEM && base.Items.Count == 1)//will delete the last item.
{
if (base.DropDownHeight == m_maxDropDownHeight)
{
base.DropDownHeight = m_itemDropDownHeight;
}
base.WndProc(ref m);
}
else
{
base.WndProc(ref m);
}
}
Method WndProc
captures four messages: mouse click and doubleclick, item add and delete, it will set DrawMode = OwnerDrawFixed
and IntegralHeight = true
when Items
is empty, and will set DrawMode = OwnerDrawVariable
and IntegralHeight = false
when Items
is not empty. DropDownHeight
will be set according to whether Items
is empty or not at the same time.
If Items
is empty and we click or doubleclick the ComboBox
, WndProc
will set DrawMode = OwnerDrawFixed
and IntegralHeight = true
if they are not these values, and disable current click or doubleclick message (i.e. does not call base.WndProc(ref m)
), then set base.DroppedDown = true
which will raise the ComboBox
to drop down once more and show empty dropdown box. This is the solution to fix the bug.
Conclusion
Some ideas and code of MultiColumnComboBoxEx
are copied from the two articles introduced above.
We mainly solve three problems:
- Set height of box and item separately
- Handle the case when Item is empty
- Draw multiple columns in box
By the way, we provide some new features too apart from fixing some small bugs in their code. This is version 1.1. Version 1.0 is published at Chinese csdn blog. Usage, comments and suggestions are welcome.
History
- 1st February, 2009: Version 1.0
- 7th February, 2009: Version 1.1
- 10th February, 2009: Source code updated
- 18th February, 2009: Version 1.2