
Introduction
I always wanted to use a multi-column ListBox in a way similar to the ListBox provided through Microsoft Access - several columns of information for which an entire row will get selected. Unfortunately the Visual Studios never saw it fit to add this- and it doesn't look like we'll get this in 2005 either from the VB Express Beta. Recently I was thinking of making controls and this one popped into my head. So, here is the control for you to enjoy.
There was one requirement that I wanted to put on it. I wanted the development environment to look at it as a ListBox
, so it had to be inherited from the ListBox
control. Now this at first was limiting on what you could do, because it essentially is a ListBox
. The biggest hurdle was the scrollbar. I had to override the Paint
method which eliminates the scrollbar and since it is inherited from a ListBox
, simply adding a scrollbar object will not work- so the scrollbar had to be completely drawn and handled by the code.
Using the control
The control resides in the namespace CWControls
- this is important for adding items at runtime. The use of the control is very simple once you've added it to your Toolbox. Add it to your form just like you do with any other control.
You'll notice several properties have been added. I'll go through each of these properties one by one:
Columns
This specifies the number of columns the ListBox
will show. This value cannot be less than 1. *Note*- you can have more columns stored in the Items
property than this number, then they just won't show.
ColumnWidths
This array specifies the width of the columns in the ListBox
. The last column displayed however, has an automatic width adjusted to the edge of the ListBox
. The unit is pixels. *Note*- make your ListBox
wide enough, I have not yet implemented horizontal scrolling.
SelectedTextRow
This property is not available in the Properties window. It returns the entire selected row delimited by an optional delimiter (default is a space).
SelectedText
This property returns the Text
of the item - or column 1.
SelectedValue
This property belonged to the original ListBox
- it now returns the Value
you set for an item.
SelectedItem
Again this property was part of the ListBox
, it now returns the selected item as a McListBoxItem
type.
Items
The meat of the matter. Items
is no longer an object type and just can't support anything going into it. It is of type McListBoxItem
.
You'll notice that the DrawMode
property is still there. Yes I've implemented the different states of the DrawMode
property so that you can do your own drawing of the control.
You can edit Items
from the Properties window, but not like the string collections. The Items
consists of the following parts:
Value
The Value
is never shown, but can be more useful than an Index value returned for searching in databases.
Text
This is what is displayed in column 1 as the main item.
SubItems
This is a dynamic string array. Put as many strings in here as you need for all your columns.
Here is a sample for adding items at runtime:
For i As Integer = 0 To max
Dim x As New CWControls.MCListBoxItem
x.Text = i
ReDim x.SubItems(1)
For t As Integer = 0 To 1
x.SubItems(t) = i & " - " & t
Next
McListBox1.Items.Add(x)
Next
Scrolling
For those who are interested in the scrolling process, I'll let my code do the talking for the most part (sorry I'm not a great commenter). I'll explain a few parts here.
There are several parts that need to be drawn. Now while drawing you have to be aware of its state (Up, Down, or Hover). The items that need to be drawn are: Up arrow, Down Arrow, Thumb, Back.
The Up and Down arrows, along with the Thumb each have the three states mentioned above. The back has two states Normal or pressed.
In order to give it these states we need to watch the OnMouseMove
and OnMouseLeave
events for Hover, and the OnMouseDown
and OnMouseUp
events for Pressed (and selecting items) over the different parts of the scrollbar.
Originally I had the entire item list drawn at one time to a bitmap and saved for scrolling different parts into view at one time, however that proved to be too much for the control when there were hundreds of items. Here the control draws only what it needs and stores the current items shown in a buffer.
In order to scroll nicely, we need to use a separate thread that is terminated when MouseUp
is fired. Loops and global variables do that trick nicely. The thread itself is global, so it can put itself to sleep and we can have different scroll speeds (first item scrolling scrolls slower than that of multiple).
MouseMove
also helps us in scrolling by selecting and dragging the thumb. To see how it was done, look into the region with the label "UI Handlers - Scrolling".
Conclusion
I hope you find this control useful, whether you're using the control or are just interested in the scrolling example. I had fun writing it (and trying to figure out how to do the various parts).
History
- 7/7/2005
- Updated
OnPaint
and JumpScroll
methods to fix the scrolling issue while scrolling past the end/beginning of the list by clicking the scrollbar background.