Monday, April 1, 2013

Using Threads in VB.Net for showing a progress bar in a separate dialog

I am new to VB.NET. I had programmed in Visual Basic 4 many many years ago. Recently I had to use Visual Studio 2010 to create a quick application for internal usage. Now there was a task which took a long time. As per good user interface design it was obvious that a progress update should be shown to keep the end user informed and updated till the task completed.

To cut a long story short, the requirement was to let the long running task continue while a progress bar showed the progress of the task. After searching for examples, I got a basic idea on how to use threads in VB.Net to accomplish the task I had at hand. 

Fig. 1 Main Dialog


 
  Fig. 2 Separately Launched Progress Dialog

I have attached the VS2010 project for you to download and see the code. The main working parts are explained here.

Basic Steps
  • Step 1 - Create a basic Windows Form application
  • Step 2 - Create a simple Dialog and add a ProgressBar and a Label to it
  • Step 3 - Write a method which will launch a separate Thread to perform the long running task
  • Step 4 - Write Delegate methods in the Dialog

The code is self explanatory.

Code for the Main Form

Option Explicit On
Option Strict On

Imports System.Threading

' Main form which launches a long running task and 
' shows progress update in a separate dialog
Public Class MainForm
    Inherits Form

    'Flag that indcates if a process is running
    Dim isProcessRunning As Boolean = False

    'Progress bar dialog
    Dim progressDlg As New ProgressDialog


    'Long running background process with 
    'a determinate progress indicator, 
    'using a seperate modal dialog containing
    'a progress bar

    <MTAThread()> _
    Private Sub ButtonClick1_Click(ByVal sender As System.Object, _
                                   ByVal e As System.EventArgs) _
                               Handles ButtonClick1.Click

        'If a process is already running, 
        'warn the user and cancel the operation
        If isProcessRunning = True Then
            MessageBox.Show("Sorry, a process is already running.", _
                            "Status", MessageBoxButtons.OK, _
                            MessageBoxIcon.Exclamation)
            Return
        End If

        'Define a background thread and start the 
        'long running process in this separate thread
        Dim backgroundThread As _
            New Threading.Thread(AddressOf DoLongRunningProcess)

        ' Start the background process thread
        backgroundThread.Start()

        ' Open the dialog
        progressDlg.Show()
    
End Sub

     
    ' The long running process 

    Public Sub DoLongRunningProcess()
        ' Set the flag that indicates if a 
        ' process is currently running
        isProcessRunning = True
        
        ' Iterate from 0 - 100
        ' On each iteration, pause the thread 
        ' for .05 seconds, then update the 
        ' dialog's progress bar
        For n As Integer = 0 To 100
            Thread.Sleep(100)
            progressDlg.UpdateProgress(n)
        Next
        
        ' Show a dialog box that confirms the process 
        ' has completed
        MessageBox.Show("Thread completed!", "Status", _
                        MessageBoxButtons.OK, _
                        MessageBoxIcon.Information)
        'Close the dialog when user clicks OK
        progressDlg.Close()
        ' Reset the flag that indicates if a process is
        ' currently running
        isProcessRunning = False
    End Sub
  
End Class
Code for the Progress Dialog
Option Explicit On
Option Strict On
Imports System.Windows.Forms
  
' Dialog class with a ProgressBar widget to 
' show the progress of a long running task
 
Public Class ProgressDialog
    Inherits Form

    ' Delegate to update the progress 
    ' of the ProgressBar widget
    Delegate Sub DelegateUpdate(ByVal progress As Integer)
   
    ' Delegate to handle the Close event 
    ' for this dialog
    Delegate Sub DelegateClose(ByRef dialog As Form)

    ' Default constructor which initializes 
    ' this control
    Public Sub New()
        InitializeComponent()
    End Sub


    
    ' Method to update the progress bar widget. This 
    ' uses the InvokeRequired and BeginInvoke methods
    Public Sub UpdateProgress(ByVal progress As Integer)
        If ProgressBar1.InvokeRequired Then
            ProgressBar1.BeginInvoke(New DelegateUpdate(AddressOf UpdateDelegateImpl), progress)
        Else
            ProgressBar1.Value = progress
        End If
    End Sub

    ' Overloads the Close method
    Public Overloads Sub Close()
        If Me.InvokeRequired Then
            Me.BeginInvoke(New  _
                           DelegateClose(AddressOf CloseDelegateImpl), Me)
        Else
            Me.Close()
        End If
    End Sub
   
    ' Implementation of the DelegateUpdate
    Sub UpdateDelegateImpl(ByVal progress As Integer)
        ProgressBar1.Value = progress
        LabelProgress.Text = ""
        LabelProgress.Text = progress & CStr("%")
    End Sub
    
    ' Implmentation of the DelegateClose
    Sub CloseDelegateImpl(ByRef dialog As Form)
        dialog.Close()
    End Sub

End Class

Download Project
I have uploaded the project as a ZIP archive on Google Drive. Click to download.