Understanding ownerdrawn listbox + OwnerDrawVariable property
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.Blu开发者_JAVA技巧e, 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).
This blog has a solution that might help. It's in C#. It seems as though overriding OnPaint might be the way to go.
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;
}
}
Might be a bit late with this answer but why not use BeginUpdate() and EndUpdate().
精彩评论