Title: Make an owner-drawn ListBox in C#
This example shows how you can make an owner-drawn ListBox. Normally a ListBox displays a textual representation of its objects, but you can change that behavior to make it display anything you can draw. This example displays some multi-line text entries.
The form's Load event handler uses the following code to make its ListBox owner-drawn and to create some multi-line text items.
// Make the ListBox owner drawn.
private void Form1_Load(object sender, EventArgs e)
{
lstChoices.DrawMode = DrawMode.OwnerDrawVariable;
// Create some items.
lstChoices.Items.Add("Name: Mercury\nMass: 0.055 Earths\nYear: 87.9691 Earth days\nTemp: 183 °C to 427 °C");
lstChoices.Items.Add("Name: Venus\nMass: 0.815 Earths\nYear: 243 Earth days");
lstChoices.Items.Add("Name: Earth\nMass: 1.0 Earths\nYear: 365.256 Earth days");
lstChoices.Items.Add("Name: Mars\nMass: 0.107 Earths\nYear: 686.971 Earth days");
}
When you make a ListBox owner-drawn, you must provide it with two event handlers: MeasureItem to determine how big the items should be and DrawItem to draw the items. The following code shows the example's MeasureItem event handler.
// Calculate the size of an item.
private int ItemMargin = 5;
private void lstChoices_MeasureItem(object sender,
MeasureItemEventArgs e)
{
// Get the ListBox and the item.
ListBox lst = sender as ListBox;
string txt = (string)lst.Items[e.Index];
// Measure the string.
SizeF txt_size = e.Graphics.MeasureString(txt, this.Font);
// Set the required size.
e.ItemHeight = (int)txt_size.Height + 2 * ItemMargin;
e.ItemWidth = (int)txt_size.Width;
}
This code gets the ListBox from the event handler's sender parameter. It then gets the ListBox item that it should draw.
Next the code uses MeasureString to see how big the text will be when drawn. It adds twice the desired margin to the height and returns the resulting size through the e.ItemHeight and e.ItemWidth parameters.
The following code shows the program's DrawItem event handler.
// Draw the item.
private void lstChoices_DrawItem(object sender,
DrawItemEventArgs e)
{
// Get the ListBox and the item.
ListBox lst = sender as ListBox;
string txt = (string)lst.Items[e.Index];
// Draw the background.
e.DrawBackground();
// See if the item is selected.
if ((e.State & DrawItemState.Selected) ==
DrawItemState.Selected)
{
// Selected. Draw with the system highlight color.
e.Graphics.DrawString(txt, this.Font,
SystemBrushes.HighlightText, e.Bounds.Left,
e.Bounds.Top + ItemMargin);
}
else
{
// Not selected. Draw with ListBox's foreground color.
using (SolidBrush br = new SolidBrush(e.ForeColor))
{
e.Graphics.DrawString(txt, this.Font, br,
e.Bounds.Left, e.Bounds.Top + ItemMargin);
}
}
// Draw the focus rectangle if appropriate.
e.DrawFocusRectangle();
}
The code gets the ListBox and the item to draw as before. It then calls e.DrawBackground to draw an appropriate background. This automatically uses the correct color depending on whether the item is currently selected
The code then checks the e.State parameter to see if the Selected flag is set. If the flag is set, the program draws the item's text using the system's HighlightText brush. If the flag is not set, the program creates a brush using the ListBox control's foreground color and draws the item with it.
After it has drawn the item, the program calls e.DrawFocusRectangle to draw a focus rectangle around the item if appropriate.
Drawing an owner-drawn ListBox is a lot more work that letting the control handle everything for you, but it gives you a lot of flexibility. My next post will show how you can add pictures to ListBox entries.
Download the example to experiment with it and to see additional details.
|