Search code examples
c#winformslistboxondrawitem

Overriding DrawItem for ListBox - unselected items are not redrawn


This is a C# desktop application. The DrawStyle property of my ListBox is set to OwnerDrawFixed.

The problem: I override DrawItem to draw text in different fonts, and it works. But when I start resizing the form at the runtime, the selected item is drawn correctly, but the rest of them are not redrawn, causing text looking corrupt for unselected items.

Here's my code:

private void listDevices_DrawItem(object sender, DrawItemEventArgs e)
{
    e.DrawBackground();

    string textDevice = ((ListBox)sender).Items[e.Index].ToString();

    e.Graphics.DrawString(textDevice,
        new Font("Ariel", 15, FontStyle.Bold), new SolidBrush(Color.Black), 
        e.Bounds, StringFormat.GenericDefault);


    // Figure out where to draw IP
    StringFormat copy = new StringFormat(
        StringFormatFlags.NoWrap |
        StringFormatFlags.MeasureTrailingSpaces
    );
    copy.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0, textDevice.Length)});

    Region[] regions = e.Graphics.MeasureCharacterRanges(
        textDevice, new Font("Ariel", 15, FontStyle.Bold), e.Bounds, copy);

    int width = (int)(regions[0].GetBounds(e.Graphics).Width);
    Rectangle rect = e.Bounds;
    rect.X += width;
    rect.Width -= width;

    // draw IP
    e.Graphics.DrawString(" 255.255.255.255",
        new Font("Courier New", 10), new SolidBrush(Color.DarkBlue),
        rect, copy);

    e.DrawFocusRectangle();
}

listDevices.Items.Add("Device001");
listDevices.Items.Add("Device002");

Also, the item that is drawn correctly (the selected one) is flickering on form resizing. No biggie, but if anyone know why.... tnx


Solution

  • Put the following code in the Resize event:

    private void listDevices_Resize(object sender, EventArgs e) {
        listDevices.Invalidate();
    }
    

    This should cause everything to be redrawn.

    To stop the flickering, you need double buffering.

    To do this, make a new class, derived from ListBox, and put the following in the constructor:

    this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    

    Or just paste this into a code file:

    using System.Windows.Forms;
    
    namespace Whatever {
        public class DBListBox : ListBox {
            public DBListBox(): base() {
                this.DoubleBuffered = true;
                // OR
                // this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            }
        }
    }
    

    Replace "Whatever" with the namespace your project uses, or make it something more useful. AFter compiling, you should be able to add a DBListBox in the form designer.