Make an owner-drawn ListBox with pictures in C#

owner-drawn ListBox with pictures

This example extends the previous one Make an owner-drawn ListBox in C# to make an owner-drawn ListBox with pictures.

The program uses the following Planet class to represent information about the planets.

class Planet
    public string Name = "";
    public string Stats = "";
    public Image Picture = null;

    public Planet(string name, Image picture, string stats)
        Name = name;
        Stats = stats;
        Picture = picture;

When the program starts, it adds Planet objects to the ListBox. Because these are objects without a good ToString method, the ListBox cannot reasonably display them by itself, so this must be an owner-drawn ListBox.

See the previous example for the basics. This example uses the following MeasureItem event handler to allocate room to draw each entry 100 pixels tall (plus a margin).

private const int ItemMargin = 5;
private const float PictureHeight = 100f;

// Return enough space for the item.
private void lstPlanets_MeasureItem(object sender,
    MeasureItemEventArgs e)
    e.ItemHeight = (int)(PictureHeight + 2 * ItemMargin);

The following code shows how the program draws the items.

// Draw the item.
private void lstPlanets_DrawItem(object sender,
    DrawItemEventArgs e)
    // Get the ListBox and the item.
    ListBox lst = sender as ListBox;
    Planet planet = (Planet)lst.Items[e.Index];

    // Draw the background.

    // Draw the picture.
    float scale = PictureHeight / planet.Picture.Height;
    RectangleF source_rect = new RectangleF(
        0, 0, planet.Picture.Width, planet.Picture.Height);
    float picture_width = scale * planet.Picture.Width;
    RectangleF dest_rect = new RectangleF(
        e.Bounds.Left + ItemMargin, e.Bounds.Top + ItemMargin,
        picture_width, PictureHeight);
    e.Graphics.DrawImage(planet.Picture, dest_rect,
        source_rect, GraphicsUnit.Pixel);

    // See if the item is selected.
    Brush br;
    if ((e.State & DrawItemState.Selected) ==
            br = SystemBrushes.HighlightText;
        br = new SolidBrush(e.ForeColor);

    // Find the area in which to put the text.
    float x = e.Bounds.Left + picture_width + 3 * ItemMargin;
    float y = e.Bounds.Top + ItemMargin;
    float width = e.Bounds.Right - ItemMargin - x;
    float height = e.Bounds.Bottom - ItemMargin - y;
    RectangleF layout_rect = new RectangleF(x, y, width, height);

    // Draw the text.
    string txt = planet.Name + '\n' + planet.Stats;
    e.Graphics.DrawString(txt, this.Font, br, layout_rect);

    // Outline the text.

    // Draw the focus rectangle if appropriate.

The code first gets the ListBox and the item that it will be drawing. Remember that item is a Planet object. The program then uses e.DrawBackground to draw the item’s background.

Next the code figures out how it must scale the planet’s picture to make it PictureHeight pixels tall. It makes source and destination rectangles for the picture and uses them to draw the image.

The program then picks a brush to use while drawing text. If the ListBox item is selected, the program uses the standard HighlightText colored brush. If the item is not selected, the program uses a brush with the item’s foreground color.

The code next makes a RectangleF that defines the area in which text will be drawn. This is the area available to draw in to the right of the planet’s picture.

Finally the program draws the text. It finishes by drawing the rectangle showing the area the text is in to make it stand out.

As I said in my previous post, 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. You can basically draw anything you like for the items.

Download Example   Follow me on Twitter   RSS feed   Donate

This entry was posted in controls, graphics and tagged , , , , , , , , , , , . Bookmark the permalink.

5 Responses to Make an owner-drawn ListBox with pictures in C#

  1. Matheus says:

    Thanks man, this opened me doors.

  2. Petros Petrosean says:

    Very nice! Thank you so much!

  3. Chris says:

    Really helpful! Thank you, and God bless you!

  4. Kyle says:

    Very helpful! If there was multiple images with text in one item. Is it possible to select only one of the images or text?

    • RodStephens says:

      It’s a list, so you can really only select an item or not.

      However, the DrawItem event handler can draw the item however you want so, for example, you could draw an outline around just one of the images or just around the text. Or you could change the text colors. Or whatever.

      The DrawBackground draws a default background behind everything. If you don’t want that, simply don’t call DrawBackground.

Comments are closed.