Title: ProgressDialog Class
Description: Using the Windows native progress dialog.
Author: Paulo Santos
eMail: pjondevelopment@gmail.com
Environment: Windows (Win2003, Win2K, WinXP), .NET 2.0, Visual Studio 2005, COM
Keywords: User Interface, Control, Dialog, Progress Dialog

ProgressDialog Class

Screenshot for ProgressDialog Class

DISCLAIMER

The Software is provided "AS IS", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and non-infringement. in no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.

Introduction

This code is a by-product of a bigger project. In my project I needed a way of informing the user of the progress of a said task. Searching over the net I found the initial component created by sytelus, that uses a TypeLib file to create an Interop layer. By other hand, Russkie, in his project created a wrapper purely in C#.

However, although I do like C#, I feel more comfortable working in VB. So, I started porting Russkie's project to Visual Basic.

Background

I would like to call your attention to the indication that this VB version is compatible only with .NET 2.0 as stated on top of this page. It's not that I don't want to develop using .NET 1.1 the problem is that VB7 has a bug regarding the use of ComImport as can be seen on MSDN.

Although on MSDN they say that this bug affects only VB.NET 2002 it also affects the 2003 version, one more reason for the use of Visual Studio 2005.

A Different Approach

Russkie's component is a great implementation, no doubt, but it uses some concepts that, in my humble opinion, feel a little bit awkward.

For instance, when the method Start is called it not only shows the ProgressDialog but it also goes through a iterative loop calling the Callback Delegate that should process whatever function is needed.

My approach to this problem is a little bit straightforward: the main loop starts the ProgressDialog at the beginning, updates the progress value while in process (noting the process cancellation by the user), and finally closes de dialog.

His implementation also relies on the Flags property, which is a map of bits. Although I kept the Flags property, I also added a few properties that are easier recognizable than a cryptic Flags property.

ProgressDialog members

Public Enumerators

Public ConstructordlgAnimations
Lists all available default animations.
Public Enum dlgAnimations As UShort
    Custom               =   0
    SearchFlashlight     = 150
    SearchDocument       = 151
    SearchComputer       = 152
    FileMove             = 160
    FileCopy             = 161
    ToRecycleBinDelete   = 162
    FromRecycleBinDelete = 163
    PermanentDelete      = 164
    FlyingPapers         = 165
    SearchGlobe          = 166
    FileMove95           = 167
    FileCopy95           = 168
    FileDelete95         = 169
    InternetCopy         = 170
    NoAnimation          = UShort.MaxValue
End Enum
Public ConstructordlgLines
Defines all available lines.
Public Enum dlgLines As Byte
    LineOne   = 1
    LineTwo   = 2
    LineThree = 3
End Enum

Public Constructors

Public ConstructorProgressDialog Constructor
Initializes a new instance of the ProgressDialog Class.
Public Sub New(ByVal f As Form)
Public Sub New(ByVal hWnd As intPtr)

Public Properties

Public PropertyAnimation
Gets or sets the animation played by the dialog window while processing.
Public Property Animation() As dlgAnimations
Note: Setting this property after the dialog is being shown has no effect.
Public PropertyAutomaticRemainingTime
Gets or sets if the remaining time should be calculated automatically.
Public Property AutomaticRemainingTime() As Boolean
Note: Setting this property after the dialog is being shown has no effect.
Public PropertyCancelMessage
The message to be displayed if the cancellation process needs to take time.
Public Property CancelMessage() As String
Note: Setting this property after the dialog is being shown has no effect.
Public PropertyComplete
Gets or sets the amount of the job done.
Public Property Complete() As ULong
Public PropertyHideProgressBar
Gets or sets the flag that displays the progress bar.
Public Property HideProgressBar() As Boolean
Note: Setting this property after the dialog is being shown has no effect.
Public PropertyHideRemainingTime
Gets of sets the flag that displays the remaining time.
Public Property HideRemainingTime() As Boolean
Note: Setting this property after the dialog is being shown has no effect.
Public PropertyIsModal
Gets or sets the flag that forces the progress window to be modal.
Public Property IsModal() As Boolean
Note: Setting this property after the dialog is being shown has no effect.
Public PropertyTitle
The Title to be displayed on the title bar of the dialog.
Public Property Title() As String
Public PropertyTotal
Gets or sets the total size of the job.
Public Property Total() As ULong
Public PropertyUserCancelled
A System.Boolean flag that indicates if the user has cancelled the process.
Public ReadOnly Property UserCancelled() As Boolean

Public Methods

Public MethodResetTimer
Resets the internal timer.
Public Sub ResetTimer()
Public MethodSetCustomAnimation
Defines the custom animation to be used.
Public Sub SetCustomAnimation( _
                       ByVal hLibrary As IntPtr, _
                       ByVal resNumber As UShort)

This method takes the instance handle specified by hLibrary and uses the common control's animation control to open and run a silent AVI clip. There are several restrictions as to what types of AVI clips can be used:

  • Clips cannot include sound.
  • The size of the AVI clip cannot exceed 272 by 60 pixels. Smaller rectangles can be used, but they might not be properly centered.
  • AVI clips must either be uncompressed or compressed with run-length (BI_RLE8) encoding. If you attempt to use an unsupported compression type, no animation will be displayed.

Note: The SetCustomAnimation function requires a pointer to a library and the number of the resources to be used. It is beyond the scope of this documentation how to acquire such information.

Public MethodSetLineText
Sets the text to be displayed in one of the three available lines.
Public Sub SetLineText(ByVal Line As dlgLines, _
                       ByVal Text As String, _
              Optional ByVal CompactPath As Boolean)
Public MethodStart
Display the dialog and resets the internal timer.
Public Sub Start()
Public MethodStop
Closes the dialog.
Public Sub Stop()

Using the code

As I stated, using this class is pretty straightforward as can be seen on the function below.

Private Sub DoSomethingLong()

  '*
  '* Create a new instance of the ProgressDialog Class
  '*
  Dim pd As New ProgressDialog(Me)

  '*
  '* Initialize the Dialog Properties
  '*
  With pd
    .Complete = 0
    .Total = 10000
    .Title = "Title"
    .Animation = _
        ProgressDialog.dlgAnimations.PermanentDelete
  End With

  '*
  '* Starts the Dialog
  '*
  pd.Start()

  '*
  '* Do something long
  '*
  For i As UInteger = 1 To 10000
    pd.Complete += 10
    pd.SetLineText(ProgressDialog.dlgLines.LineTwo, _
            "Item No." & i)

    '*
    '* Checks for user cancellation
    '*
    If (pd.UserCancelled) Then
      Exit For
    End If

    System.Threading.Thread.Sleep(50)
  Next

  '*
  '* Closes the dialog
  '*
  pd.Stop()

End Sub

I hope you find this code useful in your projects.

How to use a custom AVI

So far, the only way to use a custom animation is to create an external resource dll and use the following code snippet.

Private Enum llFlags As UInteger
    DontResolveDllReferences = &H1
    LoadLibraryAsDatafile = &H2
    LoadWithAlteredSearchPath = &H8
    LoadIgnoreCodeAuthzLevel = &H10
End Enum

<DllImport("kernel32", _
           CharSet:=CharSet.Auto, _
           SetLastError:=True)> _
Private Shared Function LoadLibraryEx( _
     <MarshalAs(UnmanagedType.LPTStr)> _
        ByVal lpFileName As String, _
        ByVal hFile As IntPtr, _
        ByVal dwFlags As LoadLibraryExFlags) As IntPtr
End Function

Private Function ShowProgress()

    Dim pd as New ProcessDialog
    Dim hLibrary as IntPtr

    '*
    '* Defines a custom animation
    '*
    hLibrary = LoadLibraryEx( _
                "YOUR_RESOURCE_DLL_HERE" & Chr(0), _
                IntPtr.Zero, _
                llFlags.DontResolveDllReferences Or _
                llFlags.LoadLibraryAsDatafile)

    If (hLibrary <> IntPtr.Zero) Then
        pd.Animation = ProgressDialog.dlgAnimations.Custom
        pd.SetCustomAnimation(hLibrary, _
                              YOUR_RESOURCE_NUMBER_HERE)
    End If
    
    '*
    '* Now you may show the 
    '* ProgressDialog as 
    '* instructed above.
    '*

End Function