Virtual Developer Workshop: Containerized Development with Docker


Showing the progress of a copy operation is quite a common question on programming forums such as Codeguru.com. With today's article, I will shed some light on this topic for you.

Before I continue, I must explain a few of the technologies that we're going to use during the course of this article.


A delegate is simply a type that represents a reference to physical methods with the same parameter list and return type. Okay, what does that mean in simple terms? A delegate is basically a substitute for a method with the same return type and with the same signature. We make use of a delegate when we cannot access certain threads directly (as explained in this article), or when we need to ensure that managed and unmanaged code can be executed properly.

Delegates (C# Programming Guide) at MSDN has a more detailed explanation of delegates.AddHandler/AddressOf.

Here is more information on the AddressOf operator.


Here is more information on the BackgroundWorker class.

Now that you have a basic understanding of all the terms and objects you will encounter during this project, we can continue to a practical example.


Create a new Visual Basic Windows Forms project. The design should resemble Figure 1.

Figure 1: Our design


Add the required namespaces:

Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.IO

Add the following variable objects:

   Private bwCopier As New BackgroundWorker

   Private Delegate Sub ProgressChanged(ByVal info As UIProgress)
   Private Delegate Sub CopyError(ByVal err As UIError)

   Private OnChange As ProgressChanged

   Private OnError As CopyError

With the preceding code, I created a BackgroundWorker object that will be responsible for running the copy operation in the background. That is why you need the two delegates: ProgressChanged and CopyError. Lastly, I created two objects that will consume both delegates. You may have noticed that, when creating the two delegates, they refer to separate classes. Let us create them now.

UIProgress Class

Public Class UIProgress

   Public strName As String
   Public lngBytes As Long
   Public lngMaxBytes As Long

   Public Sub New(ByVal FileName As String, _
         ByVal Bytes As Long, ByVal MaxBytes As Long)
      strName = FileName
      Bytes = Bytes
      MaxBytes = MaxBytes
   End Sub

End Class

There isn't much in this class. This class is simply responsible for updating the form's interface as the copy progresses.

UIError Class

Public Class UIError

   Public strErrorMsg As String
   Public strFilePath As String
   Public drResult As DialogResult

   Public Sub New(ByVal ex As Exception, _
         ByVal FilePath As String)
      strErrorMsg = ex.Message
      strFilePath = FilePath
      drResult = DialogResult.Cancel
   End Sub

End Class

This simple class just throws an error when something goes wrong in the copy operation. Let's go on to the rest of the Form's code. Add the constructor:

   Public Sub New()


      AddHandler bwCopier.DoWork, AddressOf DoCopy
      AddHandler bwCopier.RunWorkerCompleted, _
         AddressOf WorkerCompleted

      bwCopier.WorkerSupportsCancellation = True

      OnChange = AddressOf ChangeProgress
      OnError = AddressOf ErrorThrow


   End Sub

Here, I connected the backgroundworker's event to our own methods. We will add these methods as we progress through this article.

Add the DoCopy procedure:

   Private Sub DoCopy(ByVal sender As Object, _
      ByVal e As DoWorkEventArgs)

      Dim arrExtensions As String() = _
         {"*.jpg", "*.jpeg", "*.bmp", "*.png", "*.gif"}

      Dim lstFiles As New List(Of FileInfo)

      Dim strFolderPath As String = _

      Dim diDirectory As New DirectoryInfo(strFolderPath)

      Dim lngMaxBytes As Long = 0

      For Each strExt As String In arrExtensions

         Dim fiFolder As FileInfo() = diDirectory.GetFiles(strExt, _

         For Each fiFile As FileInfo In fiFolder

            If ((fiFile.Attributes And FileAttributes.Directory) <> 0) _
               Then Continue For

            lngMaxBytes += fiFile.Length


      Dim lngBytes As Long = 0
      For Each file As FileInfo In lstFiles

            Me.BeginInvoke(OnChange, New Object() _
               {New UIProgress(file.Name, lngBytes, lngMaxBytes)})
            System.IO.File.Copy(file.FullName, "c:\temp\" + _
               file.Name, True)

         Catch ex As Exception

            Dim err As New UIError(ex, file.FullName)
            Me.Invoke(OnError, New Object() {err})
            If err.drResult = Windows.Forms.DialogResult.Cancel _
               Then Exit For

         End Try

         lngBytes += file.Length


   End Sub

Add the ChangeProgress procedure:

   Private Sub ChangeProgress(ByVal info As UIProgress)

      ProgressBar1.Value = CInt(100.0 * _
         info.lngBytes / info.lngMaxBytes)
      Label1.Text = "Copying " + info.strName

   End Sub

Add the next procedures:

   Private Sub ErrorThrow(ByVal err As UIError)

      Dim msg As String = String.Format("Error _
         copying file {0}\n{1}\nClick OK to continue _
         copying files", Err.strFilePath, Err.strErrorMsg)
      err.drResult = MessageBox.Show(msg, "Copy error", _
         MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation)

   End Sub

   Private Sub WorkerCompleted(ByVal sender As Object, _
      ByVal e As RunWorkerCompletedEventArgs)


   End Sub

   Private Sub UpdateUI(ByVal docopy As Boolean)
      Label1.Visible = docopy
      ProgressBar1.Visible = docopy

      If docopy Then Button1.Text = "Cancel" _
         Else Button1.Text = "Copy"
      Label1.Text = "Starting copy..."
      ProgressBar1.Value = 0

   End Sub

Finally, add the Button's click event:

   Private Sub Button1_Click(ByVal sender As System.Object, _
         ByVal e As System.EventArgs) Handles Button1.Click
      Dim docopy As Boolean = Button1.Text = "Copy"


      If (docopy) Then bwCopier.RunWorkerAsync() _
         Else bwCopier.CancelAsync()

   End Sub


The next time you have to use copying logic within your applications, I hope you find this article useful. Until next time, cheers!

About the Author

Hannes DuPreez

Hannes du Preez is an ex MVP for Visual Basic from 2008 to 2017. He loves technology and loves Visual Basic and C#. He loves writing articles and proving that Visual Basic is more powerful than what most believe. You are most welcome to reach him at: ojdupreez1978[at]gmail[dot]com

Related Articles


  • Possible mistake

    Posted by Fabrizio on 10/14/2017 10:25am

    Hello First of all I would like to thank you for your code, I have been very helpful But I think there is a mistake: in Class UIProgress the Bytes and MaxBytes parameters should be assigned to the lngBytes and lngMaxBytes fields? Hello and thanks Fabrizio

  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date