Article Options
Premium Sponsor
Premium Sponsor

 »  Home  »  Data Programming  »  XML  »  Producing dynamic images using GDI+ and XML in VB.NET
 »  Home  »  Windows Development  »  Producing dynamic images using GDI+ and XML in VB.NET
Producing dynamic images using GDI+ and XML in VB.NET
by Peter Custance | Published  02/04/2003 | XML Windows Development | Rating:
Peter Custance

I am currently a software programmer for a large Data Card Manufacturer in the UK. Contact me at peter.custance@id-data.co.uk or p.custance@btopenworld.com.

I have nine years experience in software programming, using C and Visual Basic on Windows and Linux. I have been using VB.NET since beta 2 and I have built 2 websites using ASP 3 and ASP.NET. Visit www.britxchange.com or my homepage at www.custec.co.uk. I am now using the Visual Studio.Net 1.0 release daily and believe it to be the best development IDE available.

 

View all articles by Peter Custance...
Producing dynamic images using GDI+ and XML in VB.NET

Article source code: chordfinder.zip

The .NET Framework includes a much improved library of graphic functions developers can utilise to display dynamic images in their applications. This article looks at how you can use an XML file to produce an image that you can change at runtime based on a selection from a Listbox control. All the code is in VB.net but is easily transposed into any .NET language. To explain the workings of XML would take a large book so this article will presume basic knowledge of XML, XPath and the .NET implimentation.

The example application is a chord book for budding guitar players. The interface consists of an image of the guitar fingerboard generated at runtime using the GDI+ classes and a ListBox Control containing chord names for the user to select the chord they wish to view. On selecting a chord the finger positions are displayed on the fingerboard image. The chord information is stored in an XML file called Chords.xml. In the example of this file below you can see that each chord contains a collection of notes. Each note has two attributes which contain the x and y positions on screen.

<?xml version="1.0" encoding="utf-8" ?>
<GDIChords>
<GDIHeader>
<DateLastModified>30-10-2002</DateLastModified>
</GDIHeader>
<Chord name='A Minor'>
<Note PosX='182' posY='35'/>
<Note PosX='140' posY='85'/>
<Note PosX='96' posY='85'/>
</Chord>
<Chord name='G Major'>
<Note PosX='224' posY='138'/>
<Note PosX='56' posY='85'/>
<Note PosX='15' posY='138'/>
</Chord>
</GDIChords>

The following image shows the first chord in the file (A Minor) displayed in the application. No picture resources are used to create this image, it is completely generated with code. Next I will explain how I used the GDI+ classes to produce it.

The main point to remember when using the GDI+ library is that it is stateless. If your form is minimalized or covered by another application, all your hard work will disappear. As the programmer you must take responsibility for refreshing the image if these re-paint events occur. The fact that the GDI+ is stateless can be viewed as an advantage. For instance if you want to clear your canvas all you need to do is refresh the form or control you are drawing on. Create a new Windows Form project in VS.net and delete the default form it creates for you. Add a new windows form and name it frmMain.vb with a FixedBorderStyle of Fixed3D and place a ListBox control at the bottom. We will be using the form surface itself as a canvas but you could use a Panel or any other control which as a re-paint event. Change the form BackColor to LemonChiffon or whatever you like but please no bright pink ones!

Before we can create the image we need to program the class which will do all the drawing for us so add a new Class file to the project and call it GDIDraw.vb. Place the following code inside this file.

Imports System.Drawing
Imports System.Drawing.Drawing2D

Public Class GDIDraw

    Private mobGraphics As Graphics
    Private mobPen As Pen

    Public Sub New(ByVal obGraphics As Graphics)
        MyBase.new()
        Try
            mobGraphics = obGraphics
            mobPen = New Pen(Color.Transparent)
        Catch obEx As Exception
            Throw obEx
        End Try
    End Sub


    Public Sub DrawSphere(ByVal vnX As Integer_
                            ByVal vnY As Integer_
                            ByVal vnX2 As Integer_
                            ByVal vnY2 As Integer_
                            ByVal vColOutline As Color_
                            ByVal vColorA As Color_
                            ByVal vColorB As Color)

        Dim obBrush As SolidBrush
        Dim obLBrush As LinearGradientBrush
        Dim obRect As Rectangle
        Try
            '//DRAW SPHERE.
            mobPen.Color = Color.Black
            obBrush = New SolidBrush(vColOutline)
            obRect = New Rectangle()
            With obRect
                .X = vnX
                .Y = vnY
                .Width = vnX2
                .Height = vnY2
            End With
            obLBrush = New LinearGradientBrush(obRectvColorAvColorB_
                LinearGradientMode.ForwardDiagonal)
            mobGraphics.FillEllipse(obLBrushobRect)

            '//DRAW HIGHLIGHT.
            With obRect
                .X = vnX + 4
                .Y = vnY + 4
                .Width = vnX2 \ 6
                .Height = vnY2 \ 6
            End With
            mobPen.Color = Color.White
            obBrush.Color = Color.White
            mobGraphics.DrawEllipse(mobPenobRect)
            mobGraphics.FillEllipse(obBrushobRect)
        Catch obEx As Exception
            Throw obEx
        End Try
    End Sub

    Public Sub DrawGradientRectangle(ByVal vsngX As Single_
                                        ByVal vsngY As Single_
                                        ByVal vsngX2 As Single_
                                        ByVal vsngY2 As Single_
                                        ByVal vColorA As Color_
                                        ByVal vColorB As Color_
                                        ByVal vMode As LinearGradientMode)
        Dim obBrush As LinearGradientBrush
        Dim obRect As RectangleF
        Try
            obRect = New RectangleF()
            With obRect
                .X = vsngX
                .Y = vsngY
                .Width = vsngX2
                .Height = vsngY2
            End With
            obBrush = New LinearGradientBrush(obRectvColorAvColorB_
                vMode)
            mobGraphics.FillRectangle(obBrushobRect)
            obBrush.Dispose()
            obBrush = Nothing
        Catch obEx As Exception
            Throw obEx
        End Try
    End Sub

End Class
Generated using PrettyCode.Encoder

There are only two subs within this class plus the constructor which takes as a parameter a graphics object which represents the drawing surface. The DrawSphere sub is responsible for drawing gradient circles which also draws a white highlight to add to the 3D effect. This works by creating a rectangle to act as a holding box for an ellipse which is filled using a brush object. A smaller ellipse is placed over this to create the highlight. The DrawGradientRectangle sub is used to draw everything else including the fingerboard, frets and strings. This works by simply creating a rectangle and filling with a gradient brush. You can see how much easier this is than the old API calls in VB6. Don`t be afraid to experiment using different colors and GradientModes etc. This is all part of the fun of using the GDI+.

Next add a module file to the project, name it modMain.vb an place the following code inside.

Imports System.Drawing
Imports System.Xml

Module Main

    Public gfrmMain As frmMain
    Private mobGDIDraw As GDIDraw
    Private mobXmlDocument As XmlDocument

    '//APPLICATION STARTS HERE.
    Public Sub Main()
        Try
            gfrmMain = New frmMain()
            gfrmMain.ShowDialog()
        Catch obEx As Exception
            MsgBox(obEx.ToStringMsgBoxStyle.Critical)
            End
        End Try
    End Sub

    Public Sub DrawGuitarFingerBoard(ByRef obCanvas As Form)
        Try
            Dim obGraphics As Graphics = gfrmMain.CreateGraphics
            mobGDIDraw = New GDIDraw(obGraphics)

            With mobGDIDraw
                '//DRAW GUITAR FINGERBOARD.
                .DrawGradientRectangle(2020220240_
                        Color.Cornsilk_
                        Color.BurlyWood_
                        Drawing.Drawing2D.LinearGradientMode.Horizontal)

                '//DRAW GUITAR FRETS.
                .DrawGradientRectangle(20202208_
                        Color.White_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Vertical)
                .DrawGradientRectangle(20702208_
                        Color.White_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Vertical)
                .DrawGradientRectangle(201202208_
                        Color.White_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Vertical)
                .DrawGradientRectangle(201702208_
                        Color.White_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Vertical)
                .DrawGradientRectangle(202202208_
                        Color.White_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Vertical)

                '//DRAW STRINGS.
                .DrawGradientRectangle(23208240_
                        Color.Yellow_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Horizontal)
                .DrawGradientRectangle(235203240_
                        Color.Yellow_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Horizontal)
                .DrawGradientRectangle(64207240_
                        Color.Yellow_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Horizontal)
                .DrawGradientRectangle(106206240_
                        Color.Yellow_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Horizontal)
                .DrawGradientRectangle(150206240_
                        Color.Yellow_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Horizontal)
                .DrawGradientRectangle(193204240_
                        Color.Yellow_
                        Color.DimGray_
                        Drawing.Drawing2D.LinearGradientMode.Horizontal)
            End With
        Catch obEx As Exception
            Throw obEx
        End Try
    End Sub

    Public Function GetChord(ByVal vsChordName As StringAs XmlNodeList
        Dim nCount As Integer
        Dim nNodeCount As Integer
        Dim sNodeXPath As String
        Dim nYPosition As Integer
        Dim nXPosition As Integer
        Dim obNode As XmlNode
        Dim obNodeList As XmlNodeList

        Try
            sNodeXPath = "GDIChords/Chord[@name='" & vsChordName & "']/Note"
            obNodeList = mobXmlDocument.SelectNodes(sNodeXPath)
            If Not obNodeList Is Nothing Then
                For nCount = 0 To obNodeList.Count - 1
                    nXPosition = CType(obNodeList.Item(nCount).Attributes_
                        0).InnerTextInteger)
                    nYPosition = CType(obNodeList.Item(nCount).Attributes_
                        1).InnerTextInteger)
                    mobGDIDraw.DrawSphere(nXPositionnYPosition2525_
                        Color.BlackColor.WhiteColor.Black)
                Next
            End If
        Catch obEx As Exception
            Throw obEx
        End Try
    End Function

    Public Sub LoadChordXML()
        Try
            Dim sFilename As String = Application.StartupPath & "\Chords.xml"
            mobXmlDocument = New XmlDocument()
            mobXmlDocument.Load(sFilename)
        Catch obEx As Exception
            Throw obEx
        End Try
    End Sub

    Public Sub GetChordList(ByRef rlstBox As ListBox)
        Dim nCount As Integer
        Try
            Dim obNodeList As XmlNodeList = mobXmlDocument.SelectNodes_
                "GDIChords/Chord")
            If Not obNodeList Is Nothing Then
                For nCount = 0 To obNodeList.Count - 1
                    rlstBox.Items.Add(CType(obNodeList.Item_
                        nCount).Attributes(0).InnerTextString))
                Next
            End If
        Catch obEX As Exception
            Throw obEX
        End Try
    End Sub

End Module
Generated using PrettyCode.Encoder

You will notice in this module that we are controlling the execution of this application from a sub main procedure so right-click on your project, select properties and choose Sub Main as your Start Up Object. This module also contains procedures for reading in the Chord.xml file so you should create the XML file at the top of this article now using NotePad or your favorite Text or XML Editor. Save this XML file in the output directory (usually 'bin\Debug\' or 'bin\Release\' or just 'bin') of your project.

Next we need to add code to our frmMain to make things happen! It is not absolutely neccesary to add the Exit and About Menus so I will leave this for you to decide. Rename your ListBox control 'lstChords' and add the following code to your form.

Imports System.Environment
Imports System.Text

Public Class frmMain
    Inherits System.Windows.Forms.Form
    Private mobChordName As String

    Protected Overrides Sub OnPaint_
        ByVal e As System.Windows.Forms.PaintEventArgs)
        Try
            DrawGuitarFingerBoard(Me)
            GetChord(mobChordName)
        Catch obEx As Exception
            Throw obEx
        End Try

    End Sub

    Private Sub mnuAbout_Click(ByVal sender As System.Object_
        ByVal e As System.EventArgsHandles mnuAbout.Click
        Try
            Dim obStringBuilder As New StringBuilder()
            With obStringBuilder
                .Append(Chr(32), 4)
                .Append(Me.Text)
                .Append(vbCrLf)
                .Append("CLR Version: ")
                .Append(Environment.Version.ToString)
                MsgBox(.ToStringMsgBoxStyle.InformationMe.Text)
            End With
            obStringBuilder = Nothing
        Catch obEx As Exception
            MsgBox(obEx.Message.ToStringMsgBoxStyle.CriticalMe.Text)
        End Try
    End Sub

    Private Sub lstChords_SelectedIndexChanged_
        ByVal sender As System.Object_
        ByVal e As System.EventArgsHandles lstChords.SelectedIndexChanged
        Try
            mobChordName = CType(Me.lstChords.SelectedItemString)
            Me.Refresh()
            DrawGuitarFingerBoard(Me)
            GetChord(mobChordName)
        Catch obEx As Exception
            MsgBox(obEx.Message.ToStringMsgBoxStyle.CriticalMe.Text)
        End Try
    End Sub

    Private Sub mnuExit_Click(ByVal sender As System.Object_
        ByVal e As System.EventArgsHandles mnuExit.Click
        Try
            Me.Close()
            Application.Exit()

        Catch obEx As Exception
            MsgBox(obEx.Message.ToStringMsgBoxStyle.CriticalMe.Text)
        End Try
    End Sub

    Private Sub frmMain_Closed(ByVal sender As Object_
        ByVal e As System.EventArgsHandles MyBase.Closed
        Try
            Application.Exit()
        Catch obEx As Exception
            MsgBox(obEx.Message.ToStringMsgBoxStyle.CriticalMe.Text)
            End
        End Try
    End Sub
End Class
Generated using PrettyCode.Encoder

Open up the 'Windows Form Designer generated code' and modify the Sub New as below:

    Public Sub New()
        MyBase.New()

        'This call is required by the Windows Form Designer.
        InitializeComponent()

        'Add any initialization after the InitializeComponent() call
        Try
            Me.Text = "GDI+ Chord Finder " & _
                Application.ProductVersion.Substring(03)
            LoadChordXML()
            GetChordList(Me.lstChords)
        Catch obEx As Exception
            MsgBox(obEx.Message.ToStringMsgBoxStyle.CriticalMe.Text)
        End Try
    End Sub
Generated using PrettyCode.Encoder

You should add an AssemblyInfo file and set the Version Attribute if you wish to display this correctly. Feel free to modify/experiment with the procedures in this example. I hope it will inspire you to use the GDI+ in your applications.

How would you rate the quality of this article?
1 2 3 4 5
Poor Excellent
Tell us why you rated this way (optional):

Article Rating
The average rating is: No-one else has rated this article yet.

Article rating:3.275 out of 5
 40 people have rated this page
Article Score25275
Sponsored Links