Jump to content
Xtreme .Net Talk

ListBox Inheritance? DrawItem? Gah, my head hurts.


Recommended Posts

Posted

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.

Posted (edited)

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:

'------------------------------------------------------------------------------
'/ <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

Edited by Derek Stone
Guest TheIrishCoder
Posted

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.

Guest TheIrishCoder
Posted

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.

Posted

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?

Visit http://www.nico.gotdns.com

 

Now ONLINE!

Posted

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.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...