Custom Combobox class - Line Weight Picker - item image problem

PatM

Well-known member
Joined
Dec 5, 2012
Messages
52
Programming Experience
10+
I'm attempting to make some custom classes (comboboxes) for setting MSChart attributes and I'm stuck on line weights. I had done experiments in a sub where I'd create a bitmap/imagelist of different line weights and add that to the combobox. Once that was working I started on a custom class to do the line weights. I'm almost there but I seem to be doing all the drawing on the first item's graphic area while the rest are empty. I can't seem to figure out how to specify each item's graphic instead of just the first one. Can anyone spot the problem?

Here's the class code

VB.NET:
Public Class LineWeightComboBox
    Inherits ComboBox

    Public Property LineWeight As Integer
    Public Property MinWeight As Integer = 1
    Public Property MaxWeight As Integer = 3

    Public Sub New()

        Dim intCount As Integer
        Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
        Me.DropDownStyle = ComboBoxStyle.DropDownList

        For intCount = 1 To 3
            MyBase.Items.Add(intCount)
        Next

    End Sub

    Private Sub LineWeightComboBox_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles Me.DrawItem
        Dim pPen As Pen
        Dim gr As Graphics = e.Graphics
        Dim fFont As Font = Me.Font
        Dim w As Integer = e.Bounds.Bottom - e.Bounds.Top

        If e.Index >= 0 Then
            pPen = New Pen(Color.Black, e.Index)
            Debug.Print("e.index" & e.Index)
            gr.Clear(e.BackColor)
            gr.DrawLine(pPen, e.Bounds.Left, CInt(e.Bounds.Height / 2), e.Bounds.Width, CInt(e.Bounds.Height / 2))
        End If
    End Sub
End Class
 
The Graphics object provided is for the whole control area, e.Bounds identifies the item area. As such, if you call Graphics.Clear you will erase everything.
The Y arguments for your DrawLine is also wrong, Height is the item height (here 15), but you are not considering the Y value of the item. The first item starts at Y=0, second Y=15, etc.
One more, you are creating lots of Pen objects, you must call the Dispose method when you are done drawing with each one, or you can use the Using code block.

About items, your constructor adds some items, this will cause the form designer to generate code for adding those as well, so you will get the items added twice, first when the control instance is created and second when form adds same items. To fix this you can hide this property from designer serialization by shadowing it and apply DesignerSerializationVisibility attribute.
More about items, you are creating the Pen from item index, where it should probably be the item value; CInt(Items(e.Index)).
 
I got it almost perfect. The only problem remaining is the one you mentioned above about extra items in the item listing. I have no clue how to apply this shadowing method you mention. About all I can see using shadow is property statements and I can't figure out how you'd apply that to this situation?

VB.NET:
Public Class LineWeightComboBox
    Inherits ComboBox

    Public Property LineWeight As Integer
    Public Property MinWeight As Integer = 1
    Public Property MaxWeight As Integer = 5

    Public Sub New()

        Dim intCount As Integer
        Me.DrawMode = Windows.Forms.DrawMode.OwnerDrawFixed
        Me.DropDownStyle = ComboBoxStyle.DropDownList
        For intCount = MinWeight To MaxWeight
            Me.Items.Add(intCount)
        Next

    End Sub

    Private Sub LineWeightComboBox_DrawItem(ByVal sender As Object, ByVal e As System.Windows.Forms.DrawItemEventArgs) Handles Me.DrawItem

        Dim gr As Graphics = e.Graphics
        Dim fFont As Font = Me.Font
        Dim intLineHeight As Integer = (e.Bounds.Bottom - e.Bounds.Top) / 2
        Dim w As Integer = e.Bounds.Bottom - e.Bounds.Top

        If e.Index > -1 And e.Index <= MaxWeight Then
            If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
                gr.FillRectangle(SystemBrushes.Highlight, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width, e.Bounds.Height)
            Else
                gr.FillRectangle(SystemBrushes.Window, e.Bounds.Left, e.Bounds.Top, e.Bounds.Width, e.Bounds.Height)
            End If
            Dim pPen As Pen = New Pen(Color.Black, Me.Items(e.Index))
            gr.DrawLine(pPen, e.Bounds.Left, e.Bounds.Top + intLineHeight, e.Bounds.Width, e.Bounds.Top + intLineHeight)
            pPen.Dispose()
        End If
    End Sub
End Class
 
About all I can see using shadow is property statements and I can't figure out how you'd apply that to this situation?
You can use Shadows on all type members. The idea here is that Items property is not overridable, so you can't change the base behaviour in inherited class. You use Shadows here to hide the base property, so you declare a Items property in your class that has same signature as the base property, but add Shadows to it. You can see the declaration here: ComboBox.Items Property (System.Windows.Forms)
In property getter just return the base Items object.
Here's the DesignerSerializationVisibilityAttribute Class (System.ComponentModel) help page also, though it's not hard to find.
 
Ok I think I'm starting to get this (maybe). Using shadows you basically create a new property to replace (or overlay?) the default property. Your code uses this added property while the default one just sits there unused. Basically the same thing as override but you don't end up running the code twice and having too many items?

I'll do some experimenting and google up "Serialization". I thought serialization just referred to serializing data to send out a port (serial, net, etc).
 
Using shadows you basically create a new property to replace (or overlay?)
Yes, it is also called 'hide by name'.
I thought serialization just referred to serializing data
Whenever you do things in designer the IDE serializes code for that into the .Designer.vb partial class file. With the mentioned attribute you can prevent the designer to add code for that property.
 
Back
Top