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
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.
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:
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
public class MessageListBox : ResizableListBox
this.MeasureItem += new MeasureItemEventHandler(
Add a event handler and set the
private void MeasureItemHandler(
object sender, MeasureItemEventArgs e)
item = (ParseMessageEventArgs) Items[e.Index];
int LinesFilled, CharsFitted;
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,
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)