Introduction
After submersing myself into the Internet in the endless
search of a MultiColumn ComboBox, and trying for 2 years to develop one (in
VB6), I have finally got something half-way decent. Some thanks goes out to
Carlos H. Perez whose code has shown me how to do some very interesting things!
There is not one routine though that has been "copied" from any Project out
there, everything has been re-written (mainly because I wanted to learn) and
optimized as best I knew how.
Background
The Multi-Column ComboBox
is an Owner Drawn control derived from the
System.Windows.Forms.ComboBox
. However, the
control does not use the standard "Items" array within the normal ComboBox
.
This is where this control excels. Since I
was writing my own Control Suite, I needed a way for every Control that had a
need for a Collection of Items to be universal among all Controls. Also, I
hated
Microsoft for taking away Keys
from the
Collection Classes...so I wrote my own using ideas from the
CollectionGen
utility. The Collections used within the ComboBox
(and also
the ListView
shown above) are Strongly-Typed and very
fast, this enables a fast lookup on the Control using either the Index or the
Key of any Item.
One of the harder things to figure out was the HorizontalExtent
of
the drop-down List. When Items
exceed the Horizontal Extent, the Horizontal Scrollbar should appear. I did not
find anything anywhere on how to do this, so I did some hacking and came up
with
a very easy method using the CreateParams
property of the control. By setting the WS_HSCROLL
style bit, it gives the
Control the ability (not the functionality) to have a Horizontal Scrollbar. The
tough part is determining how much of a HorizontalExtent
to give the drop-down
list! I was able to set the HExtent by Overriding the OnDropDown
Event
of the ComboBox
.
Calculating the Horizontal Extent
The Control needed an auto-magic way of Calculating the
Horizontal Extent. Since I hate relying on Users to do the "right thing" (or
other programmers in this case), I decided to make the Control "smart" enough
to
handle things where someone leaves off. When adding a ListColumn
object
to the Control (the Control NEEDS
at least 1 ListColumn in order to Display data), if the programmer omits the
"Width" Property, the Width will remain at "-1". By leaving the Column with a
Width of -1, that Column's Width will be auto-magically calculated based on the
Widest Text in that particular Column. If the Column is set to "Visible =
False", then it's width will not be added into the Extent.
Using the code
The Control uses a very simple Object Model. It
basically works the same way as the ListView does in "Details" mode. First, the
ListColumns
are added, then the ListItems
are added.
The ListItem
Object supports a ListSubItems
Collection
which is used to access the
other Columns within the Control (again, exactly like the ListView).
Private Sub FormLoad(ByVal sender As Object,
ByVal e As System.EventArgs) Handles MyBase.Load
With cboBase
.ListColumns.Add("State ID")
.ListColumns.Add("State Name")
For i As Integer = 0 To 19
.ListItems.Add("Item" & i.ToString, "Item " & i.ToString)
.ListItems(i).ListSubItems.Add("Item" & i.ToString & ":SubItem 0")
Next i
End With
End Sub
Points of Interest
Unfortunately, this control does not support
DataBinding
. There is one thing that may be very Interesting
though.
The Control has a Special Event "FocusChanging
". I needed an Event
in
one of my interfaces that told me which Direction the User was Tabbing through
the Controls. This allowed me to control which Tab got focus on a
TabbedDialog
control so that Data Entry would be a lot easier. When
the Event is Raised, it tells which Direction the User has Tabbed.
If
you want to Handle that Event, and set Focus to another Control,
simply tell the EventArgs
that you Handled it (i.e.
e.Handled =
True
).
Private Sub cboBox3_FocusChanging(ByVal Sender As Object,
ByVal e As CodeSamples.Controls.FocusChangeEventArgs)
Handles cboBox3.FocusChanging
If (e.Direction = CodeSamples.Controls.FocusDirection.Forward) Then
e.Handled = True
cboBox1.Focus()
ElseIf (e.Direction = CodeSamples.Controls.FocusDirection.Backward) Then
e.Handled = True
cboBox4.Focus()
End If
End Sub
The other Point of Interest, is that the Control fully
supports AutoTyping! It will only allow Items to be Selected that are in
the List. The Good news is that in VS.NET 2003, Microsoft has FINALLY got
the "e.Handled
" Property of the "KeyPressEvents
" working! This makes it
so
that the AutoTyping feature does not flicker anymore! I have also tried
in
the past to be able to set a Property so the Developer could choose which
Column
was Displayed in the Text area. However, due to some weird functionality
of the CombBox
(and Inheriting from it), this will have to come when I re-write
the entire ComboBox
from scratch LOL!
History
- Fixed the Horizontal Extent not display
correctly (make sure you have Installed the GDI fix from Microsoft!)
- Autotyping feature enhanced.