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

A ListBox Displaying (User-)Controls

Rate me:
Please Sign up or sign in to vote.
4.55/5 (10 votes)
1 Jul 2008CPOL4 min read 62.8K   2.1K   52   10
Use this Control to line up controls in a ListBox
Sample Image - ARBOScrollableListBox.png

Introduction

From time to time, I was looking for an easy way to show a collection of information in my programs. Searching around, I always ended up displaying information in a ListView. One day I found an article here on The Code Project about a multiline ListBox, but that does not really solve the problem of displaying data collections like addresses. Thus I started to write a usercontrol which should be easy to use and solve my needs. Maybe it is helpful for others, too...

Background

My first thought was: easy... take a usercontrol, make it scrollable and add each sub-control with DockStyle.Top. The rest is done by the usercontrol itself and the framework.
Ok, so 5 minutes later the first version was on start. Adding 100 controls (I created an address control displaying name, street, zip, city and phone) took about 17 seconds. no good idea... Some tests later (disabling DockStyle etc.), I started from scratch. The only thing that is equal to the first approach - it's still a usercontrol.

Speed...

The next step was to think about a method that still displays items, but won't take seconds to build the list. The solution is something like a "virtual" visible list. That means, the usercontrol holds a list of controls to be displayed and rebuilds the items to be shown each time the control scrolls.

That also means that I have to control the scrollbar on my own. To do so, I need to remember the first element on the screen and the number of elements. On each scroll event, I need to rebuild the items that are in the visible area of my usercontrol.

... More Speed

The new version of the usercontrol was nearly what I wanted it to be like. It is (nearly) fast and it can scroll items, even if the items do not have the same height.
But if I add a lot of elements (let's say...1000) it still gets a little bit slow and flickers horribly. So the next step was to add an "update" mechanism. Each time I call Update(), a member update is increased, and on each EndUpdate() it is decreased. While update is > 0, no visual things are done. And when update is 0 again, the visual things are done.

Item Selection

Now we are fast enough for a lot of items, we can add/remove them and they can be scrolled. But no one gets informed as to which item is selected.

The first thing on my mind was "let's add a click event handler". But that's not really all, because if the child control itself contains other child controls, we will never be informed about the click. so we need to register to the click handler of all child controls recursively (and deregister on deletion). So adding a control leads to calling AttachClickEvents(ChildControl).

C#
private void AttachClickEvents( Control Control ) {
    Control.Click += new EventHandler( Control_Click );
    if( Control.Controls != null ) {
        foreach( Control C in Control.Controls ) {
            AttachClickEvents( C );
        }
    }
}

Now, if any child control is clicked, we are informed about that event. we need to compare, if the clicked control is in our list of controls. If not, we need to take the parent control and do that compare, again. If we find the control in our list, we find the control for which we need to fire the ControlSelected event.

Sorting Items

The last thing that is outstanding is sorting. That is really easy. We need to have a public property for an IComparer which can be set from outside the usercontrol. Now, if that comparer is set, adding/removing a control to/from our item list needs a call of items.Sort(comparer).

Using the Code

Though we have a property to get the list of controls our usercontrol is displaying, we may not use this list to change elements, because the click event handler is not properly set. They are only set in Add(Control) and they are only removed in Remove(Control) or RemoveAt(Index).

The public methods and properties are:

C#
//constructor
    public ARBOScrollableListBox()

//properties
    public List Items                       // list of controls to display
    public bool AdaptControlWidth           // make controls width fitting
    public bool AdaptControlHeight          // make controls heights fitting
    public Control CurrentSelected          // the selected control
    public IComparer Comparer               // the comparer to use
    public OrientationEnum Orientation      // orientation of the items

//methods
    public void BeginUpdate()               // stop screen updating
    public void EndUpdate()                 // redo updating
    public void Add( Control Control )      // add a control
    public void Remove( Control Control )   // remove a control
    public void RemoveAt( int Index )       // remove a control at position "index"
    public void Clear()                     // clear list of controls
    public void Rebuild()                   // rebuild the display
    public void SelectControl( Control C )  // manually select a control

Points of Interest

Writing this control showed up some interesting points explained in the background section, like click event or speed up.

Extension 1: Orientation

By now, you can set the orientation of the items within the ListBox. That means, if you set Orientation to OrientationEnum.Vertical, you will get the list of items sorted from top to bottom (in connection with the item comparer you use). Otherwise you will get them in order from left to right.

If you set the AdaptControlWidth property to false, you will get as many columns (rows), as fit on the control (see screenshot).

Extension 2: Invisibility

Thanks to my readers, I was able to change the behaviour when the control is invisible. Some of the calculations for the scrollbars depend on the number of items visible within the control. When invisible, this leads to a "division by zero" error (corrected).

Thanks...

... to Marc Clifton, who read the article and gave me some hints, before I published it...
If you want to get good information - read his articles!

History

  • 2008-05-21 : Initial release
  • 2008-06-25 : Orientation and multi cols/rows added
  • 2008-06-29 : Article updated

License

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


Written By
Software Developer acs - it-systems
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionQuestions?????????? Pin
mojtabaNava11-Nov-12 7:19
mojtabaNava11-Nov-12 7:19 
GeneralMy vote of 5 Pin
khalleo22-Mar-12 13:51
khalleo22-Mar-12 13:51 
BugIssue with Rebuild() method Pin
ant-damage20-Aug-11 7:34
ant-damage20-Aug-11 7:34 
For example, when the list contains items, and you use the Clear() method to clear the virtual list, the control collection in panelControls.Controls doesn't get update, leaving them visible.

EDIT: if you add this line to the begin of Rebuild(), it will solve the bug:
C#
if (items.Count == 0) panelControls.Controls.Clear();

QuestionScrollbar gone crazy!! Pin
Xixles15-Jul-08 12:28
Xixles15-Jul-08 12:28 
AnswerRe: Scrollbar gone crazy!! Pin
Achim Bohmann12-Aug-08 3:31
Achim Bohmann12-Aug-08 3:31 
GeneralRe: Scrollbar gone crazy!! Pin
Thisisryan7-Jan-09 4:03
Thisisryan7-Jan-09 4:03 
QuestionDivided by zero error Pin
cakirhal27-Jun-08 1:59
cakirhal27-Jun-08 1:59 
AnswerRe: Divided by zero error Pin
Achim Bohmann27-Jun-08 10:04
Achim Bohmann27-Jun-08 10:04 
QuestionListbox doesn't follow docking style Pin
K.L.K13-Jun-08 11:18
K.L.K13-Jun-08 11:18 
AnswerRe: Listbox doesn't follow docking style Pin
Achim Bohmann26-Jun-08 9:59
Achim Bohmann26-Jun-08 9:59 

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.