Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C#

MultiColumnComboBoxEx: An Extended Data-Bound Multiple Column ComboBox

Rate me:
Please Sign up or sign in to vote.
4.20/5 (7 votes)
18 Feb 2009CPOL6 min read 75.1K   2.6K   72   17
A customized multi-column ComboBox supports separate box and item height
Click to enlarge image

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:

  • C#
    public int ItemIndexOf(string itemValue, bool ignoreCase, string columnName)
  • C#
    public int ItemIndexOf(string itemValue, string columnName)
  • C#
    public int ItemIndexOf(string itemValue, bool ignoreCase)
  • C#
    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:

C#
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:

C#
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: 

  1. Set height of box and item separately
  2. Handle the case when Item is empty
  3. 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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer CSUST
China China
College teacher and free programmer who expertises application sofwares for statistics reports,finance data handling,and MIS using Visual C#, Delphi, SQL, etc..

Comments and Discussions

 
QuestionHow do you show all columns in the texbox of combo????? Pin
Member 1382197810-Oct-18 6:34
Member 1382197810-Oct-18 6:34 
GeneralMy vote of 4 Pin
Kanasz Robert27-Sep-12 11:01
professionalKanasz Robert27-Sep-12 11:01 
Bugone more bug Pin
Ashu Mahajan19-Jul-11 1:00
Ashu Mahajan19-Jul-11 1:00 
GeneralDrop down runs off screen Pin
Bryan Duchesne25-Jul-10 4:41
Bryan Duchesne25-Jul-10 4:41 
GeneralControl not working if you choose datasource at desgin time. Pin
A bagga1-Jun-10 23:50
A bagga1-Jun-10 23:50 
GeneralProblem with DataManager [modified] Pin
Taulie6-May-10 3:29
Taulie6-May-10 3:29 
GeneralBug in MultiColumnComboBoxEx Pin
A_St25-Nov-09 1:21
A_St25-Nov-09 1:21 
GeneralRe: Bug in MultiColumnComboBoxEx Pin
A_St25-Nov-09 3:40
A_St25-Nov-09 3:40 
QuestionCan we use this with a SQL datasource Pin
Member 197457031-Mar-09 0:43
Member 197457031-Mar-09 0:43 
Hi there

Can we use this with a SQL datasource by using the smart tag -> use data bound item -> data source, display member, value member ...?

How do I add this to my existing project, please?

TIA
AnswerRe: Can we use this with a SQL datasource Pin
HU Lihui31-Mar-09 17:46
HU Lihui31-Mar-09 17:46 
QuestionRe: Can we use this with a SQL datasource (NullReferenceException) Pin
Member 197457031-Mar-09 18:29
Member 197457031-Mar-09 18:29 
AnswerRe: Can we use this with a SQL datasource (NullReferenceException) Pin
HU Lihui13-Jun-09 23:40
HU Lihui13-Jun-09 23:40 
GeneralRe: Can we use this with a SQL datasource (NullReferenceException) Pin
Basu Hullur23-May-14 8:23
Basu Hullur23-May-14 8:23 
GeneralFocus problem in the sample Pin
uinlibr17-Feb-09 22:40
uinlibr17-Feb-09 22:40 
GeneralRe: Focus problem in the sample Pin
HU Lihui18-Feb-09 5:03
HU Lihui18-Feb-09 5:03 
GeneralMultiColumnComboBoxEx: Pin
kpcito11-Feb-09 6:34
kpcito11-Feb-09 6:34 
GeneralRe: MultiColumnComboBoxEx: Pin
HU Lihui11-Feb-09 15:05
HU Lihui11-Feb-09 15:05 

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.