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 and DrawMode
is OwnerDrawVariable
. 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 normal ComboBox
which shows one empty dropdown box when Items
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 when DataSource
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 of Items.IndexOf
to find item index whether DataSource
is null
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
: The string
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 bound DataSource
. Default value is empty, which will show all columns. DisplayMultiColumnsInBox
: If it is true
and DropDownStyle
is DropDownList
, it will show multiple columns text in box without vertical line. Default value is false
. DisplayVerticalLine
: If it is true
, it will show vertical separate line in dropdown box. Default value is true
. 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 find ignoreCase
: If it is true
, the string
compare will not be case sensitive. Default ignoreCase
value is true
. columnName
: The column name whose value will be found. Default column is DisplayMember
which is the property of ComboBox
.
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 be OwnerDrawVariable
if we want to change the height of box and item separately. DrawMode
must be OwnerDrawFixed
if we want to show empty dropdown box when Items
is empty and DrowDownStyle
is DropDownList
.
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)
{
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)
{
if (base.DrawMode != DrawMode.OwnerDrawFixed)
{
base.DrawMode = DrawMode.OwnerDrawFixed;
base.DropDownHeight = m_minDropDownHeight;
base.IntegralHeight = true;
base.DroppedDown = true;
}
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
{
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)
{
if (base.DropDownHeight != m_maxDropDownHeight)
{
base.DropDownHeight = m_maxDropDownHeight;
}
base.WndProc(ref m);
}
else if (m.Msg == WM_DELETEITEM && base.Items.Count == 1)
{
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
College teacher and free programmer who expertises application sofwares for statistics reports,finance data handling,and MIS using Visual C#, Delphi, SQL, etc..