What's New
Q & A
Tip Jar
C# Helper...
Follow VBHelper on Twitter
MSDN Visual Basic Community
TitleMake a ListBox display chemical symbols in Visual Basic 2005
DescriptionThis example shows how to make a ListBox display chemical symbols in Visual Basic 2005.
KeywordsListBox, chemicals, chemical symbols, Visual Basic 2005, subscripts
CategoriesControls, VB.NET, Graphics

The form's Load event handler uses the following statement to make its ListBox owner-drawn.

    lstFormulas.DrawMode = DrawMode.OwnerDrawVariable

It then add several ChemInfo objects to the ListBox. The ChemInfo class has three fields: a Name (as in Sulfuric Acid), a Formula (as in H2SO4), and a Picture. The class also provides the following DrawChemInfo subroutine. This routine draws a chemical's formula with the numbers as subscripts.

The code starts by calculating the top line for the formula's main text and the subscripted numbers. It then draws the chemical's name and "(".

Next the code measures the strings "X" and "XX" and calculates how much extra space the MeasureString function adds at the end of a string. It needs this value to make the pieces of the formula fit closely together.

The code then uses a Regex regular expression object to find runs of letters and digits in the formula. For each run, it determines whether the run contains letters or digits and then draws the run at the appropriate position.

The code finishes by drawing a ")".

' Draw the ChemInfo's formula and name at the indicated Y
' coordinate.
Public Sub DrawChemInfo(ByVal gr As Graphics, ByVal br As _
    Brush, ByVal fnt As Font, ByVal y As Integer, ByVal hgt _
    As Integer)
    ' Find the top lines for the strings.
    Dim y0 As Integer = y + hgt \ 7
    Dim y1 As Integer = y0 + hgt \ 4

    ' Draw the name.
    gr.DrawString(Me.Name & " (", fnt, br, 0, y0)

    Dim text_size As SizeF = gr.MeasureString(Me.Name & " " & _
        "(", fnt)
    Dim x As Integer = text_size.Width

    ' Draw the formula.
    ' See how much extra space is added after strings.
    Dim size1 As SizeF = gr.MeasureString("X", fnt)
    Dim size2 As SizeF = gr.MeasureString("XX", fnt)
    Dim kern As Integer = 2 * size1.Width - size2.Width
    x -= kern

    ' Find runs of non-digits and runs of digits.
    Dim reg_exp As New Regex("(\D*)(\d*)")
    Dim a_match As Match = reg_exp.Match(Me.Formula)
    While a_match.Success
        ' Draw this match's groups.
        For g As Integer = 1 To a_match.Groups.Count - 1
            Dim grp_text As String = a_match.Groups(g).Value
            If grp_text.Length > 0 Then
                If grp_text.Substring(0, 1) >= "0" AndAlso _
                    grp_text.Substring(0, 1) <= "9" Then
                    gr.DrawString(grp_text, fnt, br, x, y1)
                    gr.DrawString(grp_text, fnt, br, x, y0)
                End If
                Dim ch_size As SizeF = _
                    gr.MeasureString(grp_text, fnt)
                x += ch_size.Width - kern
            End If
        Next g

        a_match = a_match.NextMatch()
    End While

    gr.DrawString(")", fnt, br, x, y0)
End Sub
When the ListBox needs to figure out how big one of its items should be, it raises its MeasureItem event. The event handler measures the corresponding item's name and formula, and returns its width and twice its height (to make room for the subscripts).
' Calculate the size of an item.
Private Sub lstFormulas_MeasureItem(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.MeasureItemEventArgs) _
    Handles lstFormulas.MeasureItem
    Dim lst As ListBox = DirectCast(sender, ListBox)
    Dim ci As ChemInfo = DirectCast(lst.Items(e.Index), _
    Dim item_size As SizeF = _
        e.Graphics.MeasureString( _
            ci.Name & " (" & ci.Formula & ")", lst.Font)
    e.ItemHeight = 2 * item_size.Height
    e.ItemWidth = item_size.Width
End Sub
When the ListBox needs to draw an item, it raises its DrawItem event. The event handler clears the item's background and makes the right brush for this item (depending on whether the item is selected). It finds this item's ChemInfo object and calls the object's DrawChemInfo method to draw the item. Finally it calls DrawFocusRectangle to draw a rectangle around the item if it has the focus.
' Draw an item.
Private Sub lstFormulas_DrawItem(ByVal sender As Object, _
    ByVal e As System.Windows.Forms.DrawItemEventArgs) _
    Handles lstFormulas.DrawItem
    ' Draw the background.

    ' Draw the text.
    Dim lst As ListBox = DirectCast(sender, ListBox)
    Dim ci As ChemInfo = DirectCast(lst.Items(e.Index), _

    Dim br As Brush = Nothing
    If e.State And DrawItemState.Selected Then
        br = SystemBrushes.HighlightText
        br = New SolidBrush(e.ForeColor)
    End If

    ci.DrawChemInfo(e.Graphics, br, lst.Font, e.Bounds.Top, _

    ' Draw the focus rectangle if appropriate.
End Sub
Copyright © 1997-2006 Rocky Mountain Computer Consulting, Inc.   All rights reserved.