PJ on Development
Thoughts and babbling about software development. Visual Basic, .NET platform, web, etc.
|Description:||Using the Windows native progress dialog.|
|Environment:||Windows (Win2003, Win2K, WinXP), .NET 2.0, Visual Studio 2005, COM|
|Keywords:||User Interface, Control, Dialog, Progress Dialog|
- Download Source Code - 93,08 KB
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.
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.
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
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
Defines all available lines.
Public Enum dlgLines As Byte LineOne = 1 LineTwo = 2 LineThree = 3 End Enum
Initializes a new instance of the ProgressDialog Class.
Public Sub New(ByVal f As Form) Public Sub New(ByVal hWnd As intPtr)
Gets or sets the animation played by the dialog window while processing.
Public Property Animation() As dlgAnimationsNote: Setting this property after the dialog is being shown has no effect.
Gets or sets if the remaining time should be calculated automatically.
Public Property AutomaticRemainingTime() As BooleanNote: Setting this property after the dialog is being shown has no effect.
The message to be displayed if the cancellation process needs to take time.
Public Property CancelMessage() As StringNote: Setting this property after the dialog is being shown has no effect.
Gets or sets the amount of the job done.
Public Property Complete() As ULong
Gets or sets the flag that displays the progress bar.
Public Property HideProgressBar() As BooleanNote: Setting this property after the dialog is being shown has no effect.
Gets of sets the flag that displays the remaining time.
Public Property HideRemainingTime() As BooleanNote: Setting this property after the dialog is being shown has no effect.
Gets or sets the flag that forces the progress window to be modal.
Public Property IsModal() As BooleanNote: Setting this property after the dialog is being shown has no effect.
The Title to be displayed on the title bar of the dialog.
Public Property Title() As String
Gets or sets the total size of the job.
Public Property Total() As ULong
A System.Boolean flag that indicates if the user has cancelled the process.
Public ReadOnly Property UserCancelled() As Boolean
Resets the internal timer.
Public Sub ResetTimer()
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:
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.
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)
Display the dialog and resets the internal timer.
Public Sub Start()
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