Click here to Skip to main content
15,860,859 members
Articles / Programming Languages / C#
Article

Multi Column List Box in C#

Rate me:
Please Sign up or sign in to vote.
4.16/5 (34 votes)
2 Jun 20024 min read 649.7K   17.2K   75   33
An inherited ListBox that displays each column of a DataBound ListBox

Sample Image - MultiColumnListBox.png

Introduction

When I first started with .net, I was pretty disappointed that both the ListBox and ComboBox didn't have multi-column support. More accurately the ListBox didn't implement it the way I was expecting (each Item stacks horizontally rather than vertically). I had heard Windows Forms had a rich set of Controls yet they didn't have as much functionality of Microsoft Forms 2.0 (ActiveX Library used by Office VBA). However now that I've really worked with .net and Windows Forms I'm glad the MS developers spent less time writing fancy controls and more time making it easier for other people to do so.

This is an Owner Drawn ListBox inherited from System.Windows.Forms.ListBox. Its primary function is to format each Item into multiple columns. Secondly, the client should be able to retrieve the contents of any column in any row easily. I decided the best way to do this would be to mimic the drawing of the standard DataGrid. The side effect to this is it has to be bound to a DataSource.

Features

  • Multiple Columns with configurable widths
  • Automatic calculation of HorizontalExtent based on the # of columns and their width
  • ColumnCount Property to quickly limit the # of columns without modifying the DataSource
  • 2 new Events,
    C#
    MeasureSubItem
    and
    C#
    DrawSubItem
    for client code to take ownership of Painting
  • ValueMember now corresponds to MultiColumnListBox.Value (instead of Text). Also a TextMember Property has been added that corresponds to MultiColumnListBox.Text
  • ValueIndex and TextIndex properties can be set so that Column positions can be used instead of column names
  • MatchEntryStyle enumerated property extends typed character matching for case sensitivity and complete string matching (with configurable timeout)
  • FindString() and FindStringExact() overriden to take columns into account.
  • GetItemAt() method to quickly obtain an object at a specified row and column (4 Overloads)
  • Reflection is used to determine if an object can be drawn as an Image. This includes Image Bitmap and
    byte[]
    
    (if it really is an image that is)
  • Included is a helper object DataArray with a single static method ToDataSet which converts a 1-3 dimension (Non-Jagged) array into a DataSet.

Usage

C#
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Data;
using AsYetUnnamed;

public class Form1 : System.Windows.Forms.Form
{
    private DataSet ds;
    private MultiColumnListBox listBox1;
    public Form1()
    {
        ds = DataArray.ToDataSet(new object[,]{ 
                    {"Row0, col0",  "Row0, col1" ,1},
                    {"Row00, col0", "Row1, col1" ,new object()},
                    {"Row1, col0",  "Row2, col1" ,"Some String"},
                    {"Row1a, col0", "Row3, col1" ,Rectangle.Empty},
                    {"row1aa,col0", "Row4, col1" ,1},
                    {"row0, col0",  "Row5, col1" ,1},
                    {"pow0, col0",  "Row6, col1" ,1},
                    {"Row7, col0",  "Row7, col1" ,new ExampleClass()},
                    {"Row8, col0",  "Row8, col1" ,Image.FromFile("StopLight.gif")}
                    });
            
        listBox1 = new MultiColumnListBox();
        listBox1.Parent = this;

        listBox1.DataSource = arr;            
    }
    class ExampleClass
    {
        Public override string ToString()
        {
            return "Hello from ExampleClass!!";
        }
    }

}
Will produce something similar to the above screenshot. The demo project demonstrates more features.

Challenges

Probably the biggest challenge was figuring out how Complex Databinding works on WinForms. As it turns out, anything that implements IList is enumerated to get the list of rows. The difference between ListBox and DataGrid are as follows:
ListBox: Calls oString() on each Item contained in the IList. If DisplayMember is set, it instead calls IList[index].DisplayMember.ToString()

DataSet: Instead of calling ToString() on every Item contained in the IList, it uses Reflection to find every public readable property the object exposes. It displays each one of these in a column.
However if the DataSource impliments ITypedList, it uses the property definitions supplied by ITypedList instead. When used in conjunction with an object that impliments ICustomTypeDescriptor, the ITyped list can supply non-existant properties of an object to the DataGrid.

Fortunately the DataSet, or rather DataView object implements this for you. And the DataManager already provided with ListBox has the ability to query for these Interfaces automatically.

One other annoying thing was working with SelectionMode.MultiExtended. When in this mode setting SelectedIndex on an Item that is already selected but doesn't have focus will not give focus to that Item. Also, calling ClearSelected() when in MultiExtended mode clears the Selection then Selects Item 0. I spent hours trying to figure out how to set a Mutually Exclusive Selection without forcing a repaint and without Item 0 flashing.

\\TODO:

This is definitely a work-in-progress. Aside from no designer code, lack of documentation, severe lack of code comments, and too many eaten exceptions I want to implement the following:
  • Replace ColumnWidths with ColumnStyles which in addition to width also specifies Font, Color, BackColor, maybe even a Delegate to perform single column Painting without going full-blown OwnerDraw.
  • Any thoughts on Mouse-based column Resizing? Is this exceeding the purpose of a list box?
  • RE: DataArray.ToDataSet() I would like to actually make an object that Implements ITypedList and ICustomTypeDesriptor that interprets calls to a contained Array rather than just Looping through and copying elements to a DataSet.
  • I really need to come up with a namespace....

Conclusion

Hopefully this fulfills somebody's needs besides just my own... I really appreciate any feedback, comments, suggestions, bug findings, etc.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 3 Pin
Running Fire2-Jun-11 0:20
Running Fire2-Jun-11 0:20 
GeneralProblem using MultiListbox Pin
leocode726-Jan-11 10:31
leocode726-Jan-11 10:31 
GeneralRe: Problem using MultiListbox Pin
leocode76-Feb-11 19:18
leocode76-Feb-11 19:18 
GeneralMulticolumn list box for the web. Pin
Gretna22-Aug-08 2:27
Gretna22-Aug-08 2:27 
GeneralMulti column list box column header in c# Pin
Member 381416922-Feb-07 5:32
Member 381416922-Feb-07 5:32 
AnswerRe: Multi column list box column header in c# Pin
Chris Rickard22-Feb-07 5:39
Chris Rickard22-Feb-07 5:39 
GeneralDataView as the once element Pin
Kotolom27-Oct-05 23:27
Kotolom27-Oct-05 23:27 
GeneralVery Nice Pin
docrob11-Sep-05 10:10
docrob11-Sep-05 10:10 
QuestionBut the example in the article...works? Pin
GianlucaSeno6-Jul-05 10:06
GianlucaSeno6-Jul-05 10:06 
GeneralHave a question Pin
taithien9-Jun-05 16:53
taithien9-Jun-05 16:53 
Generaleasier implementation Pin
thopra31-Jan-05 5:43
thopra31-Jan-05 5:43 
GeneralNice Work Pin
GISnet27-Jan-05 5:43
GISnet27-Jan-05 5:43 
GeneralAllow empty DataSet Pin
Erik Molenaar21-Dec-04 5:39
Erik Molenaar21-Dec-04 5:39 
GeneralRe: Allow empty DataSet Pin
GianlucaSeno10-Jul-05 8:35
GianlucaSeno10-Jul-05 8:35 
GeneralRe: Allow empty DataSet Pin
Erik Molenaar10-Jul-05 22:37
Erik Molenaar10-Jul-05 22:37 
QuestionHow to specify which columns appear Pin
patchwerk2-Sep-04 11:22
patchwerk2-Sep-04 11:22 
Questionwhat about this? Pin
M.H.YAU19-Feb-04 19:49
M.H.YAU19-Feb-04 19:49 
It is no doubt a good control ever, but pretty much like nightmare for me to see code like:

<br />
[DllImport("gdi32.dll", CharSet=CharSet.Auto)]<br />


Wondering if we can replace the following:

				IntPtr hBmp = ((Bitmap)value).GetHbitmap();	<br />
e.Graphics.DrawImageUnscaled(Image.FromHbitmap(hBmp),e.Bounds);			<br />
WinAPI.DeleteObject(hBmp);		<br />


with:

					IntPtr hBmp = ((Bitmap)value).GetHbitmap();<br />
	Image img = Image.FromHbitmap(hBmp);<br />
e.Graphics.DrawImageUnscaled(img, e.Bounds);<br />
img.Dispose();	<br />

AnswerRe: what about this? Pin
Chris Rickard20-Feb-04 8:13
Chris Rickard20-Feb-04 8:13 
QuestionUsing TAB to do multi-column listbox? Pin
Joe Chen20-Nov-03 21:00
Joe Chen20-Nov-03 21:00 
AnswerRe: Using TAB to do multi-column listbox? Pin
samir4118021-Mar-05 0:33
samir4118021-Mar-05 0:33 
GeneralRe: Using TAB to do multi-column listbox? Pin
tmn11421-Feb-07 5:29
tmn11421-Feb-07 5:29 
GeneralA much easier multi column list box... Pin
Joe Chen20-Nov-03 20:59
Joe Chen20-Nov-03 20:59 
GeneralAddItem() Pin
strahan18-Aug-03 14:54
strahan18-Aug-03 14:54 
Generalredraw the scrollbars Pin
hientrang200217-Aug-03 23:55
hientrang200217-Aug-03 23:55 
GeneralQuestion abt Datagrid and IList Pin
Sameer Khan18-Mar-03 11:21
Sameer Khan18-Mar-03 11:21 

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.