What's New
Q & A
Tip Jar
C# Helper...
Follow VBHelper on Twitter
MSDN Visual Basic Community
TitleDraw a continuous graph in a separate thread in VB .NET
DescriptionThis example shows how to draw a continuous graph in a separate thread in VB .NET.
KeywordsVB .NET, graph, graphing, thread, threading
CategoriesVB.NET, Graphics, Software Engineering
When the program starts, it draws grid lines for the graph. When the user clicks the Graph button, the program checks the m_GraphThread object to see if the graphing thread is running. If m_GraphThread is Nothing, then the program creates the new thread, making its startup routine the DrawGraph subroutine. It sets the thread's priority to BelowNormal so it won't preempt the main program and starts the thread.

If the m_GraphThread object is not Nothing, then the program stops the thread.

Private m_GraphThread As Thread

' Start drawing the graph.
Private Sub btnGraph_Click(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles btnGraph.Click
    If m_GraphThread Is Nothing Then
        ' The thread isn't running. Start it.
        AddStatus("Starting thread")

        m_GraphThread = New Thread(AddressOf DrawGraph)
        m_GraphThread.Priority = ThreadPriority.BelowNormal
        m_GraphThread.IsBackground = True

        AddStatus("Thread started")

        btnGraph.Text = "Stop"
        ' The thread is running. Stop it.
        AddStatus("Stopping thread")

        ' m_GraphThread.Join()
        m_GraphThread = Nothing

        AddStatus("Thread stopped")

        btnGraph.Text = "Start"
    End If
End Sub
When the new thread starts, it runs subroutine DrawGraph. This routine repeatedly calls subroutine NewValue to generate a value and PlotValue to plot the value. When the user clicks the Start button, the program aborts the thread and the thread's DrawGraph code catches an exception.
' Draw a graph until stopped.
Private Sub DrawGraph()
        ' Generate pseudo-random values.
        Dim y As Integer = m_Y
            ' Generate the next value.

            ' Plot the new value.
            PlotValue(y, m_Y)
            y = m_Y
    Catch ex As Exception
        AddStatus("[Thread] " & ex.Message)
    End Try
End Sub
Subroutine NewValue generates a random new value in a straightforward manner.
' Generate the next value.
Private Sub NewValue()
    ' Delay a bit before calculating the value.
    Dim stop_time As Date = Now.AddMilliseconds(20)
    Do While Now < stop_time

    ' Calculate the next value.
    Static rnd As New Random
    m_Y += rnd.Next(-4, 5)
    If m_Y < 0 Then m_Y = 0
    If m_Y >= picGraph.ClientSize.Height - 1 Then m_Y = _
        picGraph.ClientSize.Height - 1
End Sub
Subroutine PlotValue is a bit odd. It cannot directly access the program's main form because controls (including the form) can only be modified by the thread that created them.

PlotValue starts by checking the InvokeRequired property. If InvokeRequired is True, then the code is running on the thread so it must invoke the main UI thread. In that case, the code makes an array of parameters to pass to the main UI thread and invokes the main thread's PlotValue method.

At this point, the main UI thread executes PlotValue. This time InvokeRequired returns False because we're running on the main UI thread. The program uses the Graphics object's DrawImage method to move the existing graph one pixel to the left and then draws the new value.

' Plot a new value.
Private Delegate Sub PlotValueDelegate(ByVal old_y As _
    Integer, ByVal new_y As Integer)
Private Sub PlotValue(ByVal old_y As Integer, ByVal new_y _
    As Integer)
    ' See if we're on the worker thread and thus
    ' need to invoke the main UI thread.
    If Me.InvokeRequired Then
        ' Make arguments for the delegate.
        Dim args As Object() = {old_y, new_y}

        ' Make the delegate.
        Dim plot_value_delegate As PlotValueDelegate
        plot_value_delegate = AddressOf PlotValue

        ' Invoke the delegate on the main UI thread.
        Me.Invoke(plot_value_delegate, args)

        ' We're done.
        Exit Sub
    End If

    ' Make the Bitmap and Graphics objects.
    Dim wid As Integer = picGraph.ClientSize.Width
    Dim hgt As Integer = picGraph.ClientSize.Height
    Dim bm As New Bitmap(wid, hgt)
    Dim gr As Graphics = Graphics.FromImage(bm)

    ' Move the old data one pixel to the left.
    gr.DrawImage(picGraph.Image, -1, 0)

    ' Erase the right edge and draw guide lines.
    gr.DrawLine(Pens.Blue, wid - 1, 0, wid - 1, hgt - 1)
    For i As Integer = m_Ymid To picGraph.ClientSize.Height _
        Step GRID_STEP
        gr.DrawLine(Pens.LightBlue, wid - 2, i, wid - 1, i)
    Next i
    For i As Integer = m_Ymid To 0 Step -GRID_STEP
        gr.DrawLine(Pens.LightBlue, wid - 2, i, wid - 1, i)
    Next i

    ' Plot a new pixel.
    gr.DrawLine(Pens.White, wid - 2, old_y, wid - 1, new_y)

    ' Display the result.
    picGraph.Image = bm

End Sub
This program's AddStatus subroutine adds a status message to the txtStatus TextBox. Because the TextBox was created by the main UI thread, the program must check InvokeRequired as before.
' Add a status string to txtStatus.
Private Delegate Sub AddStatusDelegate(ByVal txt As String)
Private Sub AddStatus(ByVal txt As String)
    ' See if we're on the worker thread and thus
    ' need to invoke the main UI thread.
    If Me.InvokeRequired Then
        ' Make arguments for the delegate.
        Dim args As Object() = {txt}

        ' Make the delegate.
        Dim add_status_delegate As AddStatusDelegate
        add_status_delegate = AddressOf AddStatus

        ' Invoke the delegate on the main UI thread.
        Me.Invoke(add_status_delegate, args)

        ' We're done.
        Exit Sub
    End If

    txtStatus.Text &= vbCrLf & txt
    txtStatus.Select(txtStatus.Text.Length, 0)
End Sub
Notes: This example uses a Timer to update a label holding the current time. Because the graphing is performed on a separate thread, the Timer continues to operate while graphing is occurring.

Compare this example to Draw a continuous graph that starts and stops in VB .NET. That example performs roughly the same operations as this one without threading and that makes the Timer suspend operation while graphing is in progress.

Note that you can also avoid threading by calling Application.DoEvents inside the graphing loop.

Copyright © 1997-2010 Rocky Mountain Computer Consulting, Inc.   All rights reserved.