Owner-Draw ListBox problems

G

Guest

I am owner-drawing a listbox, in an attempt to create a nice list with some
custom "fields" and text layout.

Essentially it works, but I must be missing something big, conceptually,
because I get all kinds of screen artifacts and weirdness.

My general goal is: list item with a few areas for text, every other item
shaded a light color for readability, font color changes with selection. The
listbox is populated with custom structurs loaded with data. Here is my
prototype code so far:

Private Sub lstAccounts_DrawItem(ByVal sender As Object, ByVal e As
System.Windows.Forms.DrawItemEventArgs) Handles lstAccounts.DrawItem'

' Set fonts and colors
Dim cLine As New System.Drawing.Pen(System.Drawing.SystemColors.ControlDark)
Dim fLarge As New Font("Verdana", 12, FontStyle.Bold, GraphicsUnit.Pixel)
Dim fNorm As New Font("Verdana", 10, FontStyle.Regular, GraphicsUnit.Pixel)
Dim fMed As New Font("Verdana", 11, FontStyle.Regular, GraphicsUnit.Pixel)
Dim bNorm As System.Drawing.Brush

' Make Variable Defaults
Dim mX As Single = e.Bounds.Width
Dim mT As Single = e.Bounds.Top
Dim mY As Single = e.Bounds.Height
Dim m75 As Single = mX * 0.75
Dim m50 As Single = mX * 0.5
Dim objA As Account
Dim objS As System.Drawing.SizeF

' Get Object
objA = lstAccounts.Items(e.Index)

' Draw background
e.DrawBackground()
e.DrawFocusRectangle()

' Assign Pen Color
If (e.State = DrawItemState.Selected) Or (e.State = DrawItemState.Selected +
DrawItemState.Focus) Then
bNorm = System.Drawing.Brushes.White
Else
bNorm = System.Drawing.Brushes.Black
End If

' Draw Background if not selected and odd
If e.Index Mod 2 = 1 Then
If (e.State <> DrawItemState.Selected) And (e.State <>
DrawItemState.Selected + DrawItemState.Focus) Then
e.Graphics.FillRectangle(System.Drawing.SystemBrushes.ControlLight,
0, mT, mX, mT + mY)
End If
End If

' Draw Bottom Line
e.Graphics.DrawLine(cLine, 0, mT + mY, mX, mT + mY)

' Draw 75% Line, 50% line
e.Graphics.DrawLine(cLine, m75, mT, m75, mT + mY)
e.Graphics.DrawLine(cLine, m50, mT, m50, mT + mY)
e.Graphics.DrawLine(cLine, m50, mT + (mY / 2), m75, mT + (mY / 2))

' Draw Name of Item, Description
e.Graphics.DrawString(objA.Account_Name, fLarge, bNorm, 5, mT + 5)
e.Graphics.DrawString(objA.Account_Memo, fNorm, bNorm, 5, mT + 20)

' Draw Balance Information (right aligned)
objS = e.Graphics.MeasureString(objA.Account_Balance.ToString("$0.00"), fMed)
e.Graphics.DrawString(objA.Account_Balance.ToString("$0.00"), fMed, bNorm,
mX - objS.Width - 5, mT + 15)

End Sub

Like I said, this code essentially works. Start it up, add some items to
the list box, it shows correctly. However, if you resize the form, or give
focus, or remove focus, or generally interact with it the rendering goes
bannas. What am I missing? Anyone out there with more experience than me?
(Read: any experience in this regard!)

Many thanks!
 
L

Larry Lard

dan said:
I am owner-drawing a listbox, in an attempt to create a nice list with some
custom "fields" and text layout.

Essentially it works, but I must be missing something big, conceptually,
because I get all kinds of screen artifacts and weirdness.

My general goal is: list item with a few areas for text, every other item
shaded a light color for readability, font color changes with selection. The
listbox is populated with custom structurs loaded with data. Here is my
prototype code so far:

[code snipped. code is all fine IMO, except that you run without Option
Strict On, but that's not important right now. Also, all DrawItem
should have

If e.Index = -1 Then Exit Sub

at the start, because you DO get called by empty listboxes. Anyway]
Like I said, this code essentially works. Start it up, add some items to
the list box, it shows correctly. However, if you resize the form, or give
focus, or remove focus, or generally interact with it the rendering goes
bannas. What am I missing? Anyone out there with more experience than me?
(Read: any experience in this regard!)

An interesting problem. I have implemented owner drawn listboxes in the
past, and found things largely went as planned in DrawItem. So I set
about creating a minimal example of your problem. This is what I came
up with:

Create a Windows Forms app, drop a list box and a button on the form,
anchor the listbox to all four sides of the form (so that we can induce
it to redraw by resizing the form). Set the listbox DrawMode to
OwnerDrawFixed. Then here's some code:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
With ListBox1.Items
.Clear()
.Add("apple apple apple apple apple apple apple apple apple
")
.Add("banana banana banana banana banana banana banana
banana banana ")
.Add("grape grape grape grape grape grape grape grape grape
")
End With
End Sub

Private Sub ListBox1_DrawItem(ByVal sender As Object, ByVal e As
System.Windows.Forms.DrawItemEventArgs) Handles ListBox1.DrawItem
With e
If .Index = -1 Then Exit Sub

.DrawBackground()

.Graphics.DrawString(ListBox1.Items(.Index).ToString,
Me.Font, Brushes.Black, _
New PointF(.Bounds.Left, .Bounds.Top)) ' Line XXX

.DrawFocusRectangle()
End With
End Sub


Now, in this example as it stands, all the DrawItem does is duplicate
the behaviour of a normal listbox. When you resize the form, everything
WORKS.

Now, change Line XXX to

New PointF(.Bounds.Right - 10, .Bounds.Top)) ' Line XXX-2

We are now asking that each listbox item gets drawn starting 10 pixels
from the right hand edge of the list box (This means of course we don't
get to see much text, but this is just an example).

Now try it. On my machine, as I drag the form wider, *only the first
item in the listbox (apple...) gets redrawn correctly* - the second and
third lines show severe graphics redraw issues.

Trying .Right\2 gives similar results. However, something like .Left+10
is *fine*.

So where does that leave us? I call bug, but that isn't immediately
useful. The key seems to be:

****
When owner-drawing, make all your drawn stuff 'anchored' to the TOP
LEFT of e.Bounds
****

Thoughts and experiments from others welcome!
 
G

Guest

Larry,

Thanks for your help! I notice all kinds of odd behaviour, but I figured
out my original problem by accident.

For one thing, I need to invalidate the control on refresh to cause the
existing items to be redrawn.

Second thing, I was using the Top + Height as the parameter for where to
draw my bottom line on the item. The problem is that this position is
actually the first pixel of the *next* item, so item #1 is drawn, and it's
good, but when item #2 is drawn it overwrites the line drawn on the previous
item.

Thanks for your help!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top