(Print this page)

Multi columns ComboBox
Published date: Tuesday, February 1, 2005
On: Moer and Éric Moreau's web site

If you are a regular reader of my column, you know that controls provided “from the box” by Microsoft are somewhat limited. This is also the case of the ComboBox.

This month, I will show you how to create a reusable multicolumn ComboBox. Through the years, I am sure you needed that feature. What was your solution? Maybe you have concatenated all the fields into a single one to display it (but in that case, you had to forgot about alignment). Maybe you bought a third party control. Maybe you found a free control over the Net. Maybe you haven’t done it just waiting for me to write about it! Your wait is over, here I am.

My method

As I have said, the ComboBox from is limited but extendable. You have at least 2 ways of creating a multicolumn ComboBox yourself. The first method is to inherit the base ComboBox control and set the DrawMode property to OwnerDraw. This method is demonstrated on “The Code Project”. The second method is to inherit from the ComboBox again and override the DropDown event to show whatever you want to display your multiple columns. I will demonstrate this second method.

Warning

I will not copy all the code here. I will simply enumerate the major steps so that you can know how recreate a different one if you need to. Because all the code is not pasted here, I strongly suggest that you to download the source code using the link at the end of column.

Creating the drop down

To be fully reusable, we first need to create a component that will be outside your application. Create a new “Windows Control Library” project. I have named mine UTCombo. The default user control created in this project needs to be deleted, as it won’t be used.

The fComboPopup form

Add a form to this library (I named mine fComboPopup). This form will be used to host the control that will be used to display our multiple columns. I will use a ListView control. You can set the Dock property of control to Fill to cover all the space, and the View property to Details. You may also want to set some properties of the form like FormBorderStyle to None, ShowInTaskBar to False, because this form will not be used as a real form but only to display our drop down part of the ComboBox.

In the code, you will find that the only public method that has been added is the SetDataTable method. This method receives a DataTable that contains the data to display and an array of string that will be used as the header.

Some events of the Form itself have been used to close the form when the user clicks elsewhere on the form or if the Esc key is pressed.

Some events of the ListView control (KeyDown and MouseUp) are used to trigger an event (AfterRowSelectEvent) that will be used by another level of the component when an item is selected in the list and to close the Form.

The MultiColCombo class

You now need to add a new Component Class to this same project. Mine is named MultiColCombo. This is the class that will be visible to the programmer who will use our component. This class inherits from the System.Windows.Forms.ComboBox so we will not reinvent everything a ComboBox is already doing.

I have created 3 new public properties in this class.

The first one is named ColumnsToDisplay is an array of string that you need to fill will the columns’ captions.

The second property is named Table and is of DataTable type. You will pass a table containing the data to display to this property.

The last property is named SelectedRow. This property is read-only and will be used by to retrieve any value of the selected row by the client form. I will demonstrate it by using the third column (the population) in my sample.

The class overrides the DropDown event of the combo. This is required in order to display our own form in place of the regular combo drop down. When this event is called, the SetDataTable method we created in the fComboPopup form is called to initialized the headers and the data. The fComboPopup form is also position just under the current ComboBox. The fComboPopup form is finally shown.

Finally, the class catch the AterRowSelectEvent event triggered by the fComboPopup form when a value is selected. This event is responsible of setting the Text property of the ComboBox and to trigger a new event (named AfterSelectEvent) to let the client form be noticed when an item is selected. I have added this event because some other events (like SelectedValueChanged or ValueMemberChanged) where triggered before my SelectedRow property was set and thus I was not able to retrieve other cells of the selected DataRow.

That’s about that for the component. What we just created is a reusable multicolumn ComboBox. You need to compile it in order to reuse it.

Test our control

To correctly test our new ComboBox, we need to create a new project of the Windows Application type.

Figure 1: Adding our control to the toolbox

Because you compiled your control library, you can add your control to the toolbox just like any other control. Right-click the toolbox and select “Add/Remove Items…”. Your control won’t appear in the list but you can click the Browse button and navigate to your project’s bin folder and select your UTCombo.dll component and click the Open button. You will see your component checked into the “Customize Toolbox” dialog. You just need to click the OK button to close the dialog. Your ComboBox is now in the toolbox and is ready to be placed on a form.

My sample

To test the control, I have created a fictitious list of employees (only their name and their province). The province is selectable from the ComboBox. All this is using data bindings. Have I said that I was to support Data Bindings? The interface looks like this:

Figure 2: Our control in action

The Load event of the form contains this code:
'Creates and fills the 2 datatables of the dataset
CreateDataSet()

'Set the source for the ComboBox
With ComboBox1
    .Table = dsTest.Tables("Province")
    .DisplayMember = "Description"
    .ValueMember = "Code"
    .ColumnsToDisplay = New String() {"Code", "Description", "Population"}
End With

'Bind other controls
SetBinding()
The CreateDataSet method creates a DataSet containing 2 DataTables. The first table is the employees’ table with 2 columns (name and province). The second table contains the list of all Canadian province and territories (code, name, and the population). This table will be used to fill our new ComboBox.

The following lines in the Load event are those required to initialize your new ComboBox. Do you remember I told that you would have to fill the Table and the ColumnsToDisplay properties? This is where you need to do it. The DisplayMember and the ValueMember properties are those of the ComboBox we have inherited.

The last thing worth mentioning is that the AfterSelectEvent event from our ComboBox is being catch to be able to display the population of the selected row. The population is available through the SelectedRow property created earlier in our component. This is the code you need.

Private Sub ComboBox1_AfterSelectEvent() Handles ComboBox1.AfterSelectEvent
    'this event is a custom event raised by the multi-column ComboBox and 
    'is used to show how to use the selectedrow property
    lblPopulation.Text = "pop: " & _
      Convert.ToInt32(ComboBox1.SelectedRow ("Population")).ToString("#,##0")
End Sub
Is it working as expected?

Conclusion

We wanted to display multi-columns into the drop down part of the combo. We just did it. If there is other things you want to fix with the ComboBox just continue with your new component. Skies the limit!

I hope you appreciated the topic and see you next month.


(Print this page)