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.

2 comments:

Unknown said...

Hi Sunit,

Very informative demo, thank you.

But I notice there is a minor issue in your demo. Just try to click the button two times, first time, it runs without any issue, second time, it will run into "First Chance Exception ..." Error.

Below is my quick fix on "ProgressDialog.vb":
1. Line 43: Me.Close() => Me.Hide()
2. Line 56: dialog.Close() => dialog.Hide()

That's all.

Unknown said...
This comment has been removed by the author.