Filecopy Control with Progress Information
This is a Windows Forms control to copy a file with progress information
Introduction
Every time I download a file via internet browser, I see the progress information presented by the download statusbar. I wanted to have a control with the same behavior for my application development. Searching the web, I found some good sources for realizing the copy process but no control with progress information. Therefore, I started to develop my own.
Background
I found a basic idea in the article of a web coding portal as follows:
' social.msdn.microsoft.com/Forums/en-US/04bf3781-23ae-4629-9bc3-10f0cdfc8629/how-to-use-progress-bar-to-vbnet-form-for-copy-a-file?forum=Vsexpressvb
' by Paul IshakMVP
Based on this idea, I developed the control. The control is a usercontrol that hosts a progressbar and a label control.
Using the Code
In the attached ZIP file, you may find a Visual Studio 2013 project with two projects inside - the project for the usercontrol and a test project. Please compile the project and then attach the DLL file (FileCopyProgress.dll) from the bin\debug folder to the Visual Studio toolbox. If done, you may use the control as usual by dropping it onto a Windows Form.
The control has some extended customer properties. Before using the control, you may set the values of these properties by code in the parent form.
Extended Properties
Copy From | Insert complete path with full filename you like to copy. |
Copy To | Insert complete target path with filename of the duplication. |
Localized Strings | These strings are displayed by the control. |
Message Format | Build a format string that commands which message should be shown. Insert one or more command letters (uppercase) where the letter sequence is whatever. Use: |
Progressbar color | Choose a color for the progress bar |
Progressbar Style | Choose a style of the progress bar |
Show Info label | If true show it, if false hide it. |
Start copying | Set this value to true to activate copy process. |
I would like to concentrate on the important parts and leave away the self explaining parts to keep this article short.
In the section where the variables are declared, you will find some constants. We need them to compute the different units of the file length. Notice that we have a hierarchy from bytes to Terabytes done by a multiplication of 1024 bytes (= 1 KB).
' ## Constants
Const KB As Long = 1024
Const MB As Long = KB * 1000
Const GB As Long = MB * 1000
Const TB As Long = GB * 1000
Const M As Long = 60
Const H As Long = M * 60
Const D As Long = H * 24
<Description("Localized strings"), DisplayName("Localized strings"), _
Browsable(True), Category("extendedProperties")> _
Public Property msgStrings As Array
In the section of the extended properties, we will have a look at the top of each property. The property bodies are self explaining. With "DisplayName
", you can set the Property Name as it will be shown in the property grid. This name can be different from the property name in the source code. For example, to put spaces in the shown name for better reading. With "Category", you will define a new category for a better sorting of the properties in the property grid with the built in sorting function.
Now we would like to examine the main function "CopyFile" which is called by the property "START
" if its property value is set to TRUE (FileCopyProgress1.START = TRUE)
inside an event of the parent form. For example, the click event of a button.
At the beginning, we will check if the origin file to be copied is saved at the named path (Property COPYFROM
). We do this using System.IO
function exists. Next, we check if the file exists in the target folder. If it exists, we delete it with the System.IO
function file.delete
.
Private Sub CopyFile()
' (C) This code is based on an idea of a developer published:
' http://www.vb-paradise.de/index.php/Thread/29443-Datein-kopieren-mit-Fortschritt-und-verbleibender-Zeit/
Try
Cursor.Current = Cursors.WaitCursor
' check file, if exists delete it
If File.Exists(COPYTO) Then
File.Delete(COPYTO)
End If
' check original file if exists copy it
If File.Exists(COPYFROM) Then
To monitor the copy process, we would like to copy the file byte by byte. Doing it this way gives us the possibilities to compute the values of rest bytes to copy, transfer speed and remaining time.
To copy byte by byte, we need two file streams one to read the byte from the original file and the other one to write the byte to create the duplicate. We call this copying byte by byte streaming. To do this, we need a special function of .NET the filestreaming object. Before we can use it, we have to initialize the filestreaming objects.
' Open filestreams for reading original file and writing the copy
fromStream = New FileStream(COPYFROM, FileMode.Open)
toStream = New FileStream(COPYTO, FileMode.CreateNew)
The parameters of the filestreaming objects are filepath (COPYTO
and COPYFROM
) and streaming mode (Open
and CreateNew
). Remember COPYTO
and COPYFROM
are extended properties as described above.
One of the information that can be shown in the info label of our control is the (origin) file length. We get this value with the command fromstream.length
as a returned value in bytes. To shorten the text of the info label, we have to convert the file length in kilobytes/megabytes a.s.o. To do this, we use the declared constants.
' get file length in byte
total = fromStream.Length
' convert file length to KB-MB-GB-TB
If total <= KB Then
msg1 = total.ToString & " Byte"
ElseIf total > KB And total <= MB Then
intTemp = Math.Round(total / KB, 0)
msg1 = intTemp.ToString & " KB"
ElseIf total > MB And total <= GB Then
intTemp = Math.Round(total / MB, 0)
msg1 = intTemp.ToString & " MB"
ElseIf total > GB And total <= TB Then
intTemp = Math.Round(total / GB, 0)
msg1 = intTemp.ToString & " GB"
ElseIf total >= TB Then
intTemp = Math.Round(total / TB, 0)
msg1 = intTemp.ToString & " TB"
End If
Now we have to capture the current time from the computers system clock to use it as the base for computing the elapsed time. We will need this information to compute the remaining time we need. After this, we can start a loop for the byte by byte copying (read/write process).
' Get Time
startTime = DateTime.Now
Do ' copy byte by byte
' check if canceled
If cancel Then
fromStream.Close()
toStream.Close()
Exit Sub
End If
' read original byte
Dim read As Integer = fromStream.Read(buffer, 0, buffer.Length)
' create the copy and write the byte into the copy
toStream.Write(buffer, 0, read)
' monitor read status
current += read
' compute done percentage for progressbar
Dim Prozent As Integer = current * 100 / total
ProgressBar1.Value = Prozent
' compute time used
eta = startTime.Subtract(DateTime.Now)
' compute remaining kilobytes to copy and remaining time
kbRemaining = (current - total) / 1024
If eta.Seconds <> 0 Then
'KB/sec
kbs = Math.Round((current / 1024) / (eta.Seconds), 2)
secRemaining = kbRemaining / kbs
End If
After copying with fromStream.Read
/ toStream.write
, we compute the percentage of the copied bytes to set the value of the progress bar. Next, we compute the time used for copying. This will give us the value for the transfer speed and leads us direct to compute the value for the remaining time. We use math.round
function because we don't want to display the values with decimal figures.
At least we go to convert the values of the copied file length, the speed and the remaining time in the same way as we had done with the origin file length just before (see above).
Now we are ready to build the string
of the info label control. We are able to choose what value may be displayed and what value may
be hidden. This is done by setting the value of the property ShowLabel. This value contains uppercase command letters TCSR. Each letter represents one value:
T = origin file length to copy C = current file length copied, S = transfer speed and R = remaining
time. Example: To show the file length to copy the speed and the
remaining time, you have to set the property value to: TSR.
' build message string
Dim strTemp As String = ""
If InStr(msgFormat, "T") Then strTemp = msg1
If InStr(msgFormat, "C") Then
If strTemp <> "" Then strTemp = strTemp & " | "
strTemp = strTemp & msg2
End If
If InStr(msgFormat, "S") Then
If strTemp <> "" Then strTemp = strTemp & " | "
strTemp = strTemp & msg3
End If
If InStr(msgFormat, "R") Then
If strTemp <> "" Then strTemp = strTemp & " | "
strTemp = strTemp & msg4
End If
' show info
lblmsg.Text = strTemp
To initialize the values of the localized strings and the controls, we do this in the New()
Event of our user control. The localized strings can be changed by using the property msgStrings
. The strings are used to build the message of the info label control and as the title of the error message of the try
/catch
function.
Public Sub New()
' Dieser Aufruf ist für den Designer erforderlich.
InitializeComponent()
' Transfer usercontrol property values to label control
With lblmsg
.Font = Me.Font
.ForeColor = Me.ForeColor
.BackColor = Me.BackColor
.Image = Me.BackgroundImage
End With
' ini message string array
_msgStrings(0) = " Sec."
_msgStrings(1) = " Min."
_msgStrings(2) = " Hrs."
_msgStrings(3) = " Days"
_msgStrings(4) = "Error!"
End Sub
We use the font
/forecolor
/backcolor
/image
property of our usercontrol
to set the values of the hosted label control.
History
- 2014/03/25 Correction of multiplication factors of the constants
- 2014/05/07 Added picture