Click here to Skip to main content
Click here to Skip to main content

Progress form in secondary thread without blocking main window

, 22 Mar 2011
Rate this:
Please Sign up or sign in to vote.
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.

'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.

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:

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)

Share

About the Author

andreslassalle
Software Developer (Senior)
United States United States
No Biography provided

Comments and Discussions

 
Generalgreat work Pinmembersunny_0071-May-14 2:05 
QuestionFlexible for my application... PinmemberSahayapraveen1-Apr-14 0:00 
QuestionGood control PinmemberAlexiAnna15-Sep-12 21:20 
QuestionNice job Pinmemberthavaraj20-Apr-11 2:37 
GeneralThanks but Pinmemberscosta_FST30-Mar-11 3:39 
GeneralRe: Thanks but Pinmemberandreslassalle1-Apr-11 7:19 
GeneralGreat Job! PinmemberCebocadence29-Mar-11 7:29 
QuestionWhat about the ProgressBar.Style.Marquee? PinmemberOlivier Levrey28-Mar-11 23:39 
AnswerRe: What about the ProgressBar.Style.Marquee? Pinmemberandreslassalle1-Apr-11 7:21 
GeneralRe: What about the ProgressBar.Style.Marquee? PinmemberOlivier Levrey3-Apr-11 4:27 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 22 Mar 2011
Article Copyright 2011 by andreslassalle
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid