Click here to Skip to main content
15,879,535 members
Articles / Programming Languages / Visual Basic

Progress form in secondary thread without blocking main window

Rate me:
Please Sign up or sign in to vote.
4.47/5 (16 votes)
22 Mar 2011CPOL3 min read 73.6K   2.3K   38   11
This project will allow using progress forms without wrapping long processes in separate threads.

Introduction

This is my first article, plus I'm not a native English speaker.. so don't be harsh on the comments please.

Very often, I have had to add a progress form in an existing function that takes longer every day because the database is growing or for other reasons. When that is the case, I need to change the code to be executed on the background so the progress form can be shown on the screen without the application showing a "Not Responding" text on the app title. This is not easy, and is even worse when the function needs user input a few times. This progress form is not a real progress form, since the bar movement is actually independent of the time the function takes to complete. But in most cases, that time is not known and the user just needs to know that the program is doing something and he/she needs to wait. I've been developing apps for my company for a few years, but I never went to school to learn programming, so you'll have to forgive me if I am not using the right terms and descriptions in this article. For this project, I implemented the progress bar using a GIF image instead of the progress bar class because it looks nicer. It shouldn't be hard to change it to a regular progress bar if needed.

progressFormSecondaryThread.png

Background

Some cases where this can be very useful:

  • You just finished a big program and your boss tells you that in any place your app takes a few hundred milliseconds you have to show a progress bar because users will be confused if you don't show a progress bar.
  • A function is taking longer than you thought when you designed it.
  • To make your app fancier.
  • You woke up one morning loving progress bars.

Using the code

I included a library that includes everything to show and close the progress form, so just add the reference to the DLL or library project and call the Show() function to show and the Close() function to close it.

VB
'Show form
progressForm.processThread.Show("Hello Universe...")
'Close form
progressForm.processThread.Close()

Thread to open the form

When you show the progress form and you need input from the user or you open an external app while showing the progress form, the app loses focus after the progress form is closed. That is a weird but normal behavior when you open forms in another thread, so to fix that (and to avoid cross-thread problems), I added a timer that closes the form from its own thread and I use the ShowWindow() and SetForegroundWindow() functions from User32.dll to ensure the app gets its focus back after the progress form is closed.

VB
Public Class processThread
    Shared messages As New List(Of String)
    Shared t As New List(Of Threading.Thread)
    Shared f As New List(Of frmProcess)

    Public Sub New()

    End Sub


    Public Shared Sub Show(ByVal message As String)
        Try
            f.Add(New frmProcess)
            AddHandler f.Last.Disposed, AddressOf formDisposed
            t.Add(New Threading.Thread(AddressOf showForm))
            t.Last.Start(message)

        Catch ex As Exception

        End Try

    End Sub

    <Runtime.InteropServices.DllImport("User32.dll")> _
    Private Shared Function SetForegroundWindow(ByVal hWnd As IntPtr) As IntPtr
    End Function
    <Runtime.InteropServices.DllImport("User32.dll")> _
    Private Shared Function ShowWindow(ByVal hWnd As IntPtr, _
            ByVal nCmdShow As Integer) As IntPtr
    End Function

    Private Shared Sub formDisposed()
        Try
            Dim procs() As Process = _
                Process.GetProcessesByName(My.Application.Info.AssemblyName)
            If procs.Length = 1 Then
                ' the previously running instance will be at either index 0 or 1
                Dim index As Integer
                If CInt(procs(0).MainWindowHandle) <> 0 Then
                    index = 0
                Else
                    index = 1
                End If

                SetForegroundWindow(procs(index).MainWindowHandle)

                ShowWindow(procs(index).MainWindowHandle, 8) '8=SW_SHOWNA
            End If
        Catch ex As Exception

        End Try

    End Sub

    Public Shared Sub Close()
        Try
            If f.Count > 0 Then
                f.Last.TIMERON = False
                f.RemoveAt(f.Count - 1)
            End If
        Catch ex As Exception

        End Try


    End Sub

    Private Shared Sub showForm(ByVal message As String)
        Try
            If message IsNot Nothing AndAlso f.Count > 0 Then
                f.Last.ShowDialog(message)
            End If
        Catch ex As Exception

        End Try

    End Sub
End Class

Here is the progress form:

VB
Imports System.Windows.Forms

Public Class frmProcess
    Inherits System.Windows.Forms.Form
    Public MESSAGE As String
    Public TIMERON As Boolean = False

    Private Sub frmProcess_FormClosing(ByVal sender As Object, _
            ByVal e As System.Windows.Forms.FormClosingEventArgs) _
            Handles Me.FormClosing
        Me.Timer1.Enabled = False
    End Sub
    Public Sub CloseIt()
        Me.Close()
        Me.Dispose()
    End Sub
    Public Overloads Sub ShowDialog(ByVal Mensaje As String)
        CheckForIllegalCrossThreadCalls = False
        Try
            TIMERON = True
            Me.Timer1.Enabled = True
            MESSAGE = Mensaje
            Me.lblMensaje.Text = Mensaje
            Me.ShowDialog()
        Catch
        End Try
    End Sub

    Public Sub stopForm()
        TIMERON = False
    End Sub

    Private Sub frmProcess_Load(ByVal sender As System.Object, _
                ByVal e As System.EventArgs) Handles MyBase.Load

    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, _
            ByVal e As System.EventArgs) Handles Timer1.Tick
        Try
            If TIMERON = False Then
                Me.Close()
                Me.Timer1.Enabled = False
                Me.Dispose()
            Else
                Me.lblMensaje.Text = MESSAGE
            End If
        Catch ex As Exception

        End Try


    End Sub
End Class

Points of interest

This is not perfect, but it saved me a lot of time and works for almost every case in my apps. If the function where you need the progress bar is very complex (user inputs, external apps, multiple simultaneous progress bars), another approach might be better, but this probably will work.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
PraiseGreat Job! Pin
Bill Tarlton10-Dec-20 6:04
Bill Tarlton10-Dec-20 6:04 
Generalgreat work Pin
sunny_0071-May-14 2:05
sunny_0071-May-14 2:05 
QuestionFlexible for my application... Pin
Sahayapraveen1-Apr-14 0:00
Sahayapraveen1-Apr-14 0:00 
QuestionGood control Pin
AlexiAnna15-Sep-12 21:20
AlexiAnna15-Sep-12 21:20 
QuestionNice job Pin
thavaraj20-Apr-11 2:37
thavaraj20-Apr-11 2:37 
GeneralThanks but Pin
scosta_FST30-Mar-11 3:39
scosta_FST30-Mar-11 3:39 
Thank you for sharing your code.
Only a question: you dont solve the "Not Responding" problem, or I'm wrong?
GeneralRe: Thanks but Pin
andreslassalle1-Apr-11 7:19
andreslassalle1-Apr-11 7:19 
GeneralGreat Job! Pin
Cebocadence29-Mar-11 7:29
Cebocadence29-Mar-11 7:29 
QuestionWhat about the ProgressBar.Style.Marquee? Pin
Olivier Levrey28-Mar-11 23:39
Olivier Levrey28-Mar-11 23:39 
AnswerRe: What about the ProgressBar.Style.Marquee? Pin
andreslassalle1-Apr-11 7:21
andreslassalle1-Apr-11 7:21 
GeneralRe: What about the ProgressBar.Style.Marquee? Pin
Olivier Levrey3-Apr-11 4:27
Olivier Levrey3-Apr-11 4:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.