views:

89

answers:

2

I have this code for an ownerdrawn list box that is repainting too frequently. It seems to be related to trying to scroll an event with a different height than normal.

Here is the 'smallest code to reproduce the problem' that can be pasted into a blank form (erase the designer file) - VB version (c# of course does the same thing):

Public Class Form1
    Inherits System.Windows.Forms.Form
    Friend WithEvents ListBox1 As New System.Windows.Forms.ListBox

    Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As  _
                                   System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
        If e.Index = -1 Then Exit Sub
        Dim i As Integer = CType(ListBox1.Items(e.Index), Integer)
        e.DrawBackground()
        If i Mod 4 <> 0 Then
            e.Graphics.DrawString("Item #: " & i.ToString & Space(20).Replace(" ", " <Filler>"), _
                                  ListBox1.Font, Brushes.Black, 0, e.Bounds.Top)
        Else
            e.Graphics.DrawLine(Pens.Blue, 10, e.Bounds.Top + 1, 52, e.Bounds.Top + 1)
        End If
        e.DrawFocusRectangle()
    End Sub

    Private Sub ListBox1_MeasureItem(ByVal sender As Object, ByVal e As  _
                   System.Windows.Forms.MeasureItemEventArgs) Handles ListBox1.MeasureItem
        If e.Index = -1 Then Exit Sub
        If CInt(ListBox1.Items(e.Index)) Mod 4 = 0 Then
            e.ItemHeight = 2
        End If
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As  _
                           System.EventArgs) Handles MyBase.Load

        Me.ListBox1.Dock = System.Windows.Forms.DockStyle.Fill
        Me.ListBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable
        Me.ClientSize = New System.Drawing.Size(454, 899)
        Me.Controls.Add(Me.ListBox1)

        For i As Integer = 0 To 500
            ListBox1.Items.Add(i)
        Next
    End Sub
End Class

The ownerdrawn listbox draws OK but I have an app that uses a listbox of this style, it is a macro replayer.

You can choose a start spot, then click single-step, or 'autoplay'. As the macro is playing of course it steps down the listbox. When it starts scrolling, it flickers like crazy, repaining when a non-standard-sized item it hit.

Try scrolling in this reduced versino that replicates the problem - via scrollbar, or just lay on the down arrow.

Any ideas what I'm doing wrong - without having to totally reinvent the listbox? Much appreciated. (I redid this question to make it clearer).

+1  A: 

I recreated your program in C# in .NET 3.5 and .NET 2.0 and I'm not sure what I should be able to see - there was a little flicker at times, but not that bad

Here is a video of the program in action:
http://pixetell.com/p00148Ce5hdFxqn78wII8FrtMRK1Hy8f1D84KE7McLQgvrU9l35

public class Form1 : System.Windows.Forms.Form
{
    System.Windows.Forms.ListBox ListBox1 = new System.Windows.Forms.ListBox();

    public Form1() : base()
    {
        this.Load += new System.EventHandler(Form1_Load);

        ListBox1.DrawItem+=new System.Windows.Forms.DrawItemEventHandler(ListBox1_DrawItem);
        ListBox1.MeasureItem += new System.Windows.Forms.MeasureItemEventHandler(ListBox1_MeasureItem);
    }

    private void ListBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
    {
        if (e.Index == -1)
            return;

        int i = System.Convert.ToInt32(ListBox1.Items[e.Index]);

        e.DrawBackground();

        if (i % 4 != 0)
        {
            string spaces = new string(' ', 20);

            e.Graphics.DrawString(
                "Item #: " + i.ToString() + spaces.Replace(" ", " <Filler>"),
                    ListBox1.Font,
                    System.Drawing.Brushes.Black,
                    0,
                    e.Bounds.Top);
        }
        else
        {
            e.Graphics.DrawLine(
                System.Drawing.Pens.Blue,
                10,
                e.Bounds.Top + 1,
                52,
                e.Bounds.Top + 1);
        }

        e.DrawFocusRectangle();
    }

    private void ListBox1_MeasureItem(object sender, System.Windows.Forms.MeasureItemEventArgs e)
    {
        if (e.Index == -1)
            return;

        if ((System.Convert.ToInt32(ListBox1.Items[e.Index]) % 4) == 0)
        {
            e.ItemHeight = 2;
        }
    }

    private void Form1_Load(object sender, System.EventArgs e)
    {
        ListBox1.Dock = System.Windows.Forms.DockStyle.Fill;
        ListBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;

        ClientSize = new System.Drawing.Size(454, 899);
        Controls.Add(ListBox1);

        for (int i = 0; i < 500; i++)
        {
            ListBox1.Items.Add(i);
        }

        ListBox1.Visible = true;
    }
}
Eli
A: 

This blog has a solution that might help. It's in C#. It seems as though overriding OnPaint might be the way to go.

msergeant
That blog had the answer, thank you. I bow - my google-fu is good, yours is better.
FastAl