ListBox Inheritance? DrawItem? Gah, my head hurts.

  • Thread starter Thread starter Vax
  • Start date Start date
V

Vax

Guest
I'm trying to figure out how the hell to make a derived class from the ListBox that will allow me to make a ListBox with individual fonts and colors for each list item. This is quite far afield from my normal programming stuff, and I am getting a little frustrated.

I also saw this OwnerDraw thing. Never played with it, don't know the code to make this work. Maybe this is the simple solution? Can anyone give me a code snippet to show me how to use this? I'd appreciate it.
 
Look in the .NET forum at the URL I posted yesterday. If you search that page for "listbox", I'm fairly sure I saw a link to a tutorial on ownerdrawing in the .NET framework.
 
Hmmm....

I've got the code here, but I feel like an idiot. I have no idea how to implement this crap. The code tries to explain itself, but with no visual reference, I can't figure out what I need to do to modify this to my purposes. Hell, I don't even know where to put this code in my project. It looks like a class module, but I'm looking to make a static control. More help, please!


Code follows:
Code:
'------------------------------------------------------------------------------
'/ <copyright from='1997' to='2001' company='Microsoft Corporation'>
'/    Copyright (c) Microsoft Corporation. All Rights Reserved.
'/
'/    This source code is intended only as a supplement to Microsoft
'/    Development Tools and/or on-line documentation.  See these other
'/    materials for detailed information regarding Microsoft code samples.
'/
'/ </copyright>
'------------------------------------------------------------------------------
Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Drawing.Text
Imports System.Reflection
Imports System.Windows.Forms

Namespace Microsoft.Samples.WinForms.Vb.OwnerDrawListBox

    'This sample control demonstrates various properties and
    'methods for the ListBox control.

    Public Class OwnerDrawListBox
        Inherits System.Windows.Forms.Form

        'Used to paint the list box
        Private listBoxBrushes() As Brush
        Private listBoxHeights() As Integer = {50, 25, 33, 15}
        Private sansSerifFontFamily As FontFamily

        'Public Constructor
        Public Sub New()
            MyBase.New()

            sansSerifFontFamily = new FontFamily (GenericFontFamilies.SansSerif)

            ' This call is required for support of the Windows Forms Form Designer.
            InitializeComponent()

            OwnerDrawListBox = Me

            'Set up the brushes we are going to use

            'Load the image to be used for the background from the exe's resource fork
            Dim backgroundImage As Image = New Bitmap([Assembly].GetExecutingAssembly().GetManifestResourceStream("colorbars.jpg"))
            'Now create the brush we are going to use to paint the background
            Dim backgroundBrush As Brush = New TextureBrush(backgroundImage)
            Dim r As Rectangle
            r = New Rectangle(0, 0, listBox1.Width, 100)
            Dim lb As New LinearGradientBrush(r, Color.Red, Color.Yellow, LinearGradientMode.Horizontal)

            listBoxBrushes = New Brush() {backgroundBrush, Brushes.LemonChiffon, lb, Brushes.PeachPuff}
        End Sub

        'Form overrides dispose to clean up the component list.
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If Not (components Is Nothing) Then
                    components.Dispose()
                End If
            End If
            MyBase.Dispose(disposing)
        End Sub



#Region " Windows Form Designer generated code "

        'Required by the Windows Form Designer
        Private components As System.ComponentModel.IContainer
        Private WithEvents listBox1 As System.Windows.Forms.ListBox

        Private WithEvents OwnerDrawListBox As System.Windows.Forms.Form

        'NOTE: The following procedure is required by the Windows Form Designer
        'It can be modified using the Windows Form Designer.
        'Do not modify it using the code editor.
        Private Sub InitializeComponent()
            Me.components = New System.ComponentModel.Container()
            Me.listBox1 = New System.Windows.Forms.ListBox()
            Me.listBox1.ColumnWidth = 144
            Me.listBox1.ForeColor = SystemColors.WindowText
            Me.listBox1.IntegralHeight = False
            Me.listBox1.Items.AddRange(New Object() {"First", "Second", "Third", "Fourth"})
            Me.listBox1.Location = New System.Drawing.Point(8, 24)
            Me.listBox1.Size = New System.Drawing.Size(232, 200)
            Me.listBox1.TabIndex = 0
            [b]AddHandler Me.listBox1.DrawItem, AddressOf Me.listBox1_DrawItem
            AddHandler Me.listBox1.MeasureItem, AddressOf Me.listBox1_MeasureItem
            Me.listBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable[/b]
            Me.AutoScale = False
            Me.ClientSize = New System.Drawing.Size(248, 248)
            Me.Font = New System.Drawing.Font(sansSerifFontFamily, 12)
            Me.Text = "ListBox"
            Me.Controls.AddRange(New System.Windows.Forms.Control() {listbox1})
        End Sub

#End Region

        [b]Private Sub listBox1_DrawItem(ByVal sender As Object, ByVal e As DrawItemEventArgs)
            Dim brush As Brush
            Dim selected As Boolean
            Dim displayText As String

            ' The following method should generally be called before drawing.
            ' It is actually superfluous here, since the subsequent drawing
            ' will completely cover the area of interest.
            e.DrawBackground()

            'The system provides the context
            'into which the owner custom-draws the required graphics.
            'The context into which to draw is e.graphics.
            'The index of the item to be painted is e.Index.
            'The painting should be done into the area described by e.Bounds.
            brush = listBoxBrushes(e.Index)
            e.Graphics.FillRectangle(brush, e.Bounds)
            e.Graphics.DrawRectangle(SystemPens.WindowText, e.Bounds)

            If (e.State And DrawItemState.Selected) = DrawItemState.Selected Then
                selected = True
            Else
                selected = False
            End If

            displayText = "ITEM #" & e.Index

            If (selected) Then
                displayText &= " SELECTED"
            End If

            e.Graphics.DrawString(displayText, Me.Font, Brushes.Black, e.Bounds.X, e.Bounds.Y)

            e.DrawFocusRectangle()
        End Sub[/b]


        [b]'Return the height of the item to be drawn
        Private Sub listBox1_MeasureItem(ByVal sender As Object, ByVal e As MeasureItemEventArgs)
            Dim displayText As String
            Dim stringSize As SizeF

            'Work out what the text will be
            displayText = "ITEM #" & e.Index

            'Get width & height of string
            stringSize = e.Graphics.MeasureString(displayText, Me.Font)

            'Account for top margin
            stringSize.Height += 6

            If listBoxHeights(e.Index) > stringSize.Height Then
                'Now set height to taller of default and text height
                e.ItemHeight = listBoxHeights(e.Index)
            Else
                e.ItemHeight = CInt(stringSize.Height)
            End If
        End Sub[/b]

        ' The main entry point for the application.
        Public Shared Sub Main()
            System.Threading.Thread.CurrentThread.ApartmentState = System.Threading.ApartmentState.STA
            Application.Run(New OwnerDrawListBox())
        End Sub

    End Class

End Namespace
 
Last edited by a moderator:
I'm making a custom listbox control by inheriting and then overriding the onDrawItem method.

But how do you also change the properties from an item of the listbox???
 
When you overide the onDrawItem you are overiding the listboxes code for drawing an item so your code should draw the item as you want it to look. You are not overiding how the whole control looks just an individual item.

Understanding how the whole thing works is important. The first important thing is setting the ListBox's DrawMode. In the code posted here it is set to OwnerDrawVariable which means you will write the code to draw the controls elements and they can vary in size. You can also use OwnerDrawFixed which means you will write the code to draw each element but their size will be the same in all cases.
Next up you write code in the onMeasureItem event to decide the size of the element. You can omit code from this event to accept the default size. When overiding this code the main reasons will be to set it's size according to the text you will use for example. It provides a MeasureItemEventArgs object which can be used to get the graphics object, the index of the item being drawn and the item height and width.
The code in the example here is stringSize = e.Graphics.MeasureString(displayText, Me.Font) which calculates the size in pixels of the text. The code then sets the itemheight and itemwidth accordingly. You often use this code to set the item size so that an icon/bitmap you intend to draw will fit.
The next event is the DrawItem event. The code example here can be confusing as it deals with the 2 important events in the wrong order.
In the drawitem event you use the graphics object passed by the DrawItemEventArgs, e.Grpahics (any GDI+ drawing requires a graphics object), and then draw whatever you want within the bounds of the DrawItemEventArgs object - e.bounds.
 
But I want that each item from the listbox has the property for example 'Icon' so the OnDrawItem Sub can use that property from the item to draw the icon near the Item
 
If you're inheriting the listbox then add an icon property and overide the Add method to take a seond parameter i.e. an icon. and set the icon property to this.
Then when drawing just reference Me.IconProperty to get the icon.
 
But I mean more icons. So every item can have a different icon. If you have a property icon of the listbox, that's only one icon for whole the listbox!!!
 
Just think about it. You'll have to have your own collection of ListItems instead of the default and override the Add method just like TheIrishCoder suggested. It's up to you to write the code to support it.
 
Or maybe I can add an IconList(just like an ImageList). And then draw every icon in the onDrawItem sub.


But as which type of property has i to declare that property??

Arraylist, .... or what else??
 
Just make your own Add, Remove and whatever else methods, and do it however you like. You could use a Collection, Array, SortedList, it's your listbox so you decide how you want to manage the items.
 
I'm developping my control, but now I have the following problem:

I have a few color properties. In the Set statement of the properties i have: Invalidate() to redraw each item.

If I change at runtime a color property, the listbox redraws all the items. But not at designtime!!!

What now?
 
debuggers wanted...

if anyone wants to try and figure out what I'm doing wrong on this one, I'd appreciate it. I've built my derived control based out of an online magazine I stumbled onto, tried to make a few changes to streamline it et al, but naturally I screwed it up. I'm pretty sure I messed it up in the actual drawing arguments and function calls, but anyone who is particularly bored is welcome to tear apart what I have and tell me what I need to do to make it work.
 
Back
Top