Owner Drawn Comboboxes, Listboxes, and Menus in Visual Basic

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Introduction

I have always loved playing around with controls to push them to their limits. Today is no exception. This article will provide you with enough background to be able to use the Owner draw methods to add more spice to your controls.

Owner Drawing: What Is It?

Owner drawing in Windows Forms, which is also known as custom drawing, is a technique for changing the visual appearance of certain controls. Here is more information: https://msdn.microsoft.com/en-us/library/yyfab68k%28v=vs.110%29.aspx.

The MeasureItem Event

This event occurs each time an owner-drawn ComboBox item needs to be drawn and when the sizes of the list items are determined. More information: https://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.measureitem%28v=vs.110%29.aspx.

The DrawItem Event

You can use this event to perform the tasks needed to draw items in the ComboBox. If you have a variable sized item (when the ComboBox.DrawMode property is set to the OwnerDrawVariable value of System.Windows.Forms.DrawMode), before drawing an item, the MeasureItem event is raised. You can create an event handler for the MeasureItem event to specify the size for the item that you are going to draw in your event handler for the DrawItem event. Here is more information: https://msdn.microsoft.com/en-us/library/system.windows.forms.combobox.drawitem%28v=vs.110%29.aspx.

Today’s Practical Exercise

Start Visual Studio and create a new Visual Basic Windows Forms project. You may name it anything you like; you may also name all the objects on each form anything you like. Keep in mind, though, that your naming convention may be different than mine, so our object names might not be the same.

Design Form 1 to resemble Figure 1:

Combo1
Figure 1: Form 1 Design

Remember to add an Imagelist to Form 1 as well and add a few images to it. Design Form 2 to resemble Figure 2:

Combo2
Figure 2: Form 2 Design

Set the OwnerDraw Property for the Listbox to OwnerDrawVariable.

Code

Form 1

Create a Hashtable to store the list items:

   Private htListItems As New Hashtable

This will host all our list items for the combobox.

Override the Form’s Onload event:

   Protected Overrides Sub OnLoad(ByVal e As _
         System.EventArgs)
      MyBase.OnLoad(e)
      With Me.ImageList1.Images
          .Add(SystemIcons.Application)
         Me.htListItems.Add(0, "This is the " & _
            Environment.NewLine & "Application Icon")
         .Add(SystemIcons.Asterisk)
         Me.htListItems.Add(1, "This is the " & _
            Environment.NewLine & "Asterisk Icon")
         .Add(SystemIcons.Error)
         Me.htListItems.Add(2, "This is the " & _
            Environment.NewLine & "Error Icon")
         .Add(SystemIcons.Exclamation)
      End With
   End Sub

This simply loads all the items into the Hashtable. For more information regarding Overriding, read here.

Add the Form Load event:

   Private Sub Form1_Load(sender As Object, _
         e As EventArgs) Handles Me.Load
      Me.ComboBox1.Items.Clear()
      Me.ComboBox1.Text = ""
      Dim i As Integer
      Dim s As String
       For i = 0 To 2

         s = Join(Split(Me.htListItems(i), _
            Environment.NewLine), "")
         Me.ComboBox1.Items.Add(s)
      Next i
   End Sub

Inside this event, the list items are added to the Combobox, using the Split and Join string functions. For more information regarding Split and Join, please read here.

Add the Combobox’s MeasureItem event:

   Private Sub ComboBox1_MeasureItem(ByVal sender As Object, _
         ByVal e As System.Windows.Forms.MeasureItemEventArgs) _
         Handles ComboBox1.MeasureItem

      If e.Index < 0 Then Return

      Dim str As String = Me.htListItems(e.Index)
      Dim strOneLine As String = _
         Join(Split(str, Environment.NewLine), "")
      Dim img As Image = Me.ImageList1.Images(e.Index)

      Dim hgt As Single = Math.Max(img.Height, _
         e.Graphics.MeasureString(str, Me.ComboBox1.Font).Height) _
         * 1.1
      e.ItemHeight = hgt

      If Me.ComboBox1.DropDownStyle = ComboBoxStyle.DropDownList Then
         Me.ComboBox1.ItemHeight = hgt
      Else
         Me.ComboBox1.ItemHeight = _
            e.Graphics.MeasureString(strOneLine, _
            Me.ComboBox1.Font).Height * 1.1
      End If

      e.ItemWidth = Me.ComboBox1.Width
   End Sub

This event simply calculates the height of the picture (that was added earlier) along with the height of the text which also was added earlier, and ensures that the display renders it correctly depending on the different Combobox styles.

Add the DrawItem event:

   Private Sub ComboBox1_DrawItem(ByVal sender As Object, _
      ByVal e As System.Windows.Forms.DrawItemEventArgs) _
      Handles ComboBox1.DrawItem

      If e.Index < 0 Then Return
      Dim str As String = Me.htListItems(e.Index)
      Dim g As Graphics = e.Graphics
      Dim bBlue As SolidBrush = New SolidBrush(Color.Blue)
      Dim bWhite As SolidBrush = New SolidBrush(Color.White)

      Dim hgt As Single
      Dim img As Image = Me.ImageList1.Images(e.Index)
      If Me.ComboBox1.DrawMode = DrawMode.OwnerDrawFixed Then
         hgt = Math.Max(img.Height, _
            e.Graphics.MeasureString(str, _
            Me.ComboBox1.Font).Height) _
            * 1.1
         Me.ComboBox1.ItemHeight = hgt
      End If

      bBlue.Dispose()
      bWhite.Dispose()
      bBlue = Nothing
      bWhite = Nothing
   End Sub

This event simply draws the desired items into the ComboBox.

Form 2

Load the items into the ListBox:

   Private Sub Form1_Load(ByVal sender As System.Object, _
         ByVal e As System.EventArgs) Handles MyBase.Load
      ListBox1.Items.Add("One")
      ListBox1.Items.Add("Two")
      ListBox1.Items.Add("Three")
   End Sub

This is self-explanatory, but if you’re unfamiliar with the concept, the preceding code simply loads items into a ListBox.

Add the DrawItem event:

   Private Sub ListBox1_DrawItem(ByVal sender As Object, _
         ByVal e As System.Windows.Forms.DrawItemEventArgs) _
         Handles ListBox1.DrawItem
      Dim textFont As Font
      textFont = New Font(e.Font.FontFamily, e.Font.Size * 2)

      e.Graphics.DrawString(ListBox1.Items(e.Index).ToString(), _
                            textFont, _
                            New SolidBrush(e.ForeColor), _
                            e.Bounds.X, e.Bounds.Y)

      e.DrawFocusRectangle()

   End Sub

In the previous DrawItem event, you created a new Font object and set the font object’s size to double than the previous size. Lastly, the DrawString Graphics method is used to draw the Listbox item with the new font.

Conclusion

Ownerdrawing controls can be easy and fun. Enjoy playing around with them

Hannes DuPreez
Hannes DuPreez
Ockert J. du Preez is a passionate coder and always willing to learn. He has written hundreds of developer articles over the years detailing his programming quests and adventures. He has written the following books: Visual Studio 2019 In-Depth (BpB Publications) JavaScript for Gurus (BpB Publications) He was the Technical Editor for Professional C++, 5th Edition (Wiley) He was a Microsoft Most Valuable Professional for .NET (2008–2017).

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read