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.
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.
progressForm.processThread.Show("Hello Universe...")
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
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)
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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.