Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

An auto-resize C# ListBox

0.00/5 (No votes)
16 Jul 2002 1  
A custom ListBox that can resize its items automatically
Before resize After resize After another resize

What's the problem?

I had to write a little tool that validates an input file according to a file format specification. To add some visual candy to the application, I wanted to include a little status window where all relevant parsing messages (errors, warnings and so on) with some descriptive text are shown.

So the first try was to inherit from the standard ListBox class and implement custom drawing code with a little icon, a headline and the message text. To support varying text sizes I subscribed to the MeasureItem event and calculated the item heights individually for each item. This worked fine until I resized my control the first time: No more MeasureItem events!

A quick look into the documentation revealed the sentence: "Occurs when an owner-drawn ListBox is created". As I added my parsing messages to the listbox on the fly, a continuous re-creation of the control was not an option (besides being inefficient). So this was a dead end.

The solution

After playing around with some possible workarounds without success, I came to the conclusion that this would require a more fundamental approach: I had to write an own listbox that would behave like I had expected it from the standard listbox right from the beginning. The task turned out to be easier that I first expected.

The solution consists basically of two classes: ResizableListBox and MessageListBox. The former is the replacement for the standard ListBox class. It tries to mimic the real ListBox as closely as possible regarding events, properties and methods. Although I have to admit that it's not 100% complete (data binding is missing) but it should do the trick in most cases. It does not provide any additional benefit over the original listbox when used alone. The only difference is, that it fires the MeasureItem event every time the control is redrawn. The latter implements the actual custom listbox with the fancy drawing.

How to use it

First add the ResizableListBox to your project. Then create a derived class and subscribe to the MeasureItem event:

public class MessageListBox : ResizableListBox 
{
    public MessageListBox()
    {    
        InitializeComponent();                    
        this.MeasureItem += new MeasureItemEventHandler(
	    this.MeasureItemHandler);
        
        //more ctor code here

        ...
    }
        
    ...
}

Add a event handler and set the MeasureItemEventArgs.ItemHeight property:

private void MeasureItemHandler(
    object sender, MeasureItemEventArgs e)
{

    int MainTextHeight;            
    ParseMessageEventArgs item;
    item =  (ParseMessageEventArgs) Items[e.Index];
    int LinesFilled, CharsFitted;
            
    // as we do not use the same algorithm to calculate 

    // the size of the text (for performance reasons)

    // we need to add some safety margin ( the 0.9 factor ) 

    // to ensure that always all text is displayed

    int width = (int)((this.Width - m_MainTextOffset) * 0.9);
    int height = 200;
    Size sz = new Size(width , height);    

    e.Graphics.MeasureString(item.MessageText, this.Font, sz,
        StringFormat.GenericDefault, out CharsFitted, 
        out LinesFilled);
            
    MainTextHeight = LinesFilled * this.Font.Height;

    e.ItemHeight = IconList.ImageSize.Height + MainTextHeight + 4;
}

To implement your own drawing code also makes sense:

protected override void OnDrawItem( DrawItemEventArgs e)
{
    //custom drawing goes here

    ...
}

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