locked
C# Backgroundworker Filecopy with progressbar feedback

    Question

  • With reference to Tall Dude & ahmedilyas VB.NEt code to run a filecopy operation off a backgroundworker with progressbar feedback

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=833596&SiteID=1

    Here is the C# code I converted from VB.net. The conversion hasn't worked and I'm not able to figure out where the error is. I did try, so please don't flame me for this... appreciate any debugging help.

    Add ref to MS Visual Basic

    using System.IO;

    using System;

    using System.Drawing;

    using System.Collections;

    using System.ComponentModel;

    using System.Windows.Forms;

    using Microsoft.VisualBasic.Devices;

    using Microsoft.VisualBasic.FileIO; // not strictly necessary, but shortens the access to some options

     

    namespace WindowsApplication1

    {

        public partial class Form1 : Form

        {

            private ProBar _ProBar;

            private StepBar _StepBar;

            private ValueBar _ValueBar;

     

           

            public Form1()

            {

                InitializeComponent();

            }

     

     

            private void BackgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)

            {

                string[] theExtentions = {"*.jpg", "*.bmp", "*.png", "*.gif"};

                foreach (string currentExt in theExtentions)

                {

                    string[] theFiles = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), currentExt, System.IO.SearchOption.AllDirectories);

                    BeginInvoke(_ProBar, new object[] {theFiles.Length});

                    foreach (string currentFile in theFiles)

                    {

                        try

                        {

                            Computer c = new Computer();

                            c.FileSystem.CopyFile(currentFile, "c:\\tempfiles\\" + System.IO.Path.GetFileName(currentFile), true);

                            BeginInvoke(_StepBar);

                        }

                        catch (Exception ex)

                        {

                            // if any other error type occurs or the attempts are

                            // too many, do the following.

                            MessageBox.Show(ex.Message);

                            break; // TODO: might not be correct. Was : Exit Try

                        }

                    }

                    BeginInvoke(_ValueBar, new object[] { 1 });

                }

            }

            delegate void ProBar(int value);

            delegate void StepBar();

            delegate void ValueBar(int value);

            private void UpProBar(int value)

            {

                this.progressBar1.Maximum = value;

            }

            private void UpStepBar()

            {

                try

                {

                    progressBar1.PerformStep();

                }

                catch (Exception ex)

                {

                    // if any other error type occurs or the attempts are

                    // too many, do the following.

                    MessageBox.Show(ex.Message);

                    //break; // TODO: might not be correct. Was : Exit Try

                }

            }

            private void UpValueBar(int value)

            {

                this.progressBar1.Value = value;

            }

     

            private void BackgroundWorker1_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)

            {

                this.progressBar1.Visible = false;

                this.button1.Text = "Copy";

            }

     

            private void button1_Click(object sender, EventArgs e)

            {

                try

                {

                    if (this.button1.Text == "Copy")

                    {

                        this.button1.Text = "Cancel";

                        _ProBar = new ProBar(UpProBar);

                        _StepBar = new StepBar(UpStepBar);

                        _ValueBar = new ValueBar(UpValueBar);

                        this.progressBar1.Minimum = 1;

                        this.progressBar1.Value = 1;

                        this.progressBar1.Step = 1;

                        this.progressBar1.Visible = true;

                        this.backgroundWorker1.RunWorkerAsync();

                    }

                    else if ((this.button1.Text == "Cancel"))

                    {

                        this.backgroundWorker1.CancelAsync();

                        this.progressBar1.Visible = false;

                        this.button1.Text = "Copy";

                        MessageBox.Show("Cancelled");

                    }

                }

                catch (Exception ex)

                {

                    // if any other error type occurs or the attempts are

                    // too many, do the following.

                    MessageBox.Show(ex.Message);

                    //break; // TODO: might not be correct. Was : Exit Try

                }

            }

     

        }

    }

     

    Wednesday, October 18, 2006 5:48 PM

Answers

  • The Visual Basic CopyFile method is useful to let it display its own progress.  You are not using that feature.  Here is a sample program that shows another approach.  Start a new project and drop a label, button and progressbar on the form.  Then paste this code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.IO;

    namespace WindowsApplication1 {
      public partial class Form1 : Form {
        // Class to report progress
        private class UIProgress {
          public UIProgress(string name_, long bytes_, long maxbytes_) {
            name = name_; bytes = bytes_; maxbytes = maxbytes_;
          }
          public string name;
          public long bytes;
          public long maxbytes;
        }
        // Class to report exception {
        private class UIError {
          public UIError(Exception ex, string path_) {
            msg = ex.Message; path = path_; result = DialogResult.Cancel;
          }
          public string msg;
          public string path;
          public DialogResult result;
        }
        private BackgroundWorker mCopier;
        private delegate void ProgressChanged(UIProgress info);
        private delegate void CopyError(UIError err);
        private ProgressChanged OnChange;
        private CopyError OnError;

        public Form1() {
          InitializeComponent();
          mCopier = new BackgroundWorker();
          mCopier.DoWork += Copier_DoWork;
          mCopier.RunWorkerCompleted += Copier_RunWorkerCompleted;
          mCopier.WorkerSupportsCancellation = true;
          OnChange += Copier_ProgressChanged;
          OnError += Copier_Error;
          button1.Click += button1_Click;
          ChangeUI(false);
        }

        private void Copier_DoWork(object sender, DoWorkEventArgs e) {
          // Create list of files to copy
          string[] theExtensions = { "*.jpg", "*.jpeg", "*.bmp", "*.png", "*.gif" };
          List<FileInfo> files = new List<FileInfo>();
          string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
          DirectoryInfo dir = new DirectoryInfo(path);
          long maxbytes = 0;
          foreach (string ext in theExtensions) {
            FileInfo[] folder = dir.GetFiles(ext, SearchOption.AllDirectories);
            foreach (FileInfo file in folder) {
              if ((file.Attributes & FileAttributes.Directory) != 0) continue;
              files.Add(file);
              maxbytes += file.Length;
            }
          }
          // Copy files
          long bytes = 0;
          foreach (FileInfo file in files) {
            try {
              this.BeginInvoke(OnChange, new object[] { new UIProgress(file.Name, bytes, maxbytes) });
              File.Copy(file.FullName, @"c:\temp\" + file.Name, true);
            }
            catch (Exception ex) {
              UIError err = new UIError(ex, file.FullName);
              this.Invoke(OnError, new object[] { err });
              if (err.result == DialogResult.Cancel) break;
            }
            bytes += file.Length;
          }
        }
        private void Copier_ProgressChanged(UIProgress info) {
          // Update progress
          progressBar1.Value = (int)(100.0 * info.bytes / info.maxbytes);
          label1.Text = "Copying " + info.name;
        }
        private void Copier_Error(UIError err) {
          // Error handler
          string msg = string.Format("Error copying file {0}\n{1}\nClick OK to continue copying files", err.path, err.msg);
          err.result = MessageBox.Show(msg, "Copy error", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
        }
        private void Copier_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
          // Operation completed, update UI
          ChangeUI(false);
        }
        private void ChangeUI(bool docopy) {
          label1.Visible = docopy;
          progressBar1.Visible = docopy;
          button1.Text = docopy ? "Cancel" : "Copy";
          label1.Text = "Starting copy...";
          progressBar1.Value = 0;
        }
        private void button1_Click(object sender, EventArgs e) {
          bool docopy = button1.Text == "Copy";
          ChangeUI(docopy);
          if (docopy) mCopier.RunWorkerAsync();
          else mCopier.CancelAsync();
        }
      }
    }
    Saturday, October 21, 2006 5:25 PM
    Moderator

All replies

  • The Visual Basic CopyFile method is useful to let it display its own progress.  You are not using that feature.  Here is a sample program that shows another approach.  Start a new project and drop a label, button and progressbar on the form.  Then paste this code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.IO;

    namespace WindowsApplication1 {
      public partial class Form1 : Form {
        // Class to report progress
        private class UIProgress {
          public UIProgress(string name_, long bytes_, long maxbytes_) {
            name = name_; bytes = bytes_; maxbytes = maxbytes_;
          }
          public string name;
          public long bytes;
          public long maxbytes;
        }
        // Class to report exception {
        private class UIError {
          public UIError(Exception ex, string path_) {
            msg = ex.Message; path = path_; result = DialogResult.Cancel;
          }
          public string msg;
          public string path;
          public DialogResult result;
        }
        private BackgroundWorker mCopier;
        private delegate void ProgressChanged(UIProgress info);
        private delegate void CopyError(UIError err);
        private ProgressChanged OnChange;
        private CopyError OnError;

        public Form1() {
          InitializeComponent();
          mCopier = new BackgroundWorker();
          mCopier.DoWork += Copier_DoWork;
          mCopier.RunWorkerCompleted += Copier_RunWorkerCompleted;
          mCopier.WorkerSupportsCancellation = true;
          OnChange += Copier_ProgressChanged;
          OnError += Copier_Error;
          button1.Click += button1_Click;
          ChangeUI(false);
        }

        private void Copier_DoWork(object sender, DoWorkEventArgs e) {
          // Create list of files to copy
          string[] theExtensions = { "*.jpg", "*.jpeg", "*.bmp", "*.png", "*.gif" };
          List<FileInfo> files = new List<FileInfo>();
          string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
          DirectoryInfo dir = new DirectoryInfo(path);
          long maxbytes = 0;
          foreach (string ext in theExtensions) {
            FileInfo[] folder = dir.GetFiles(ext, SearchOption.AllDirectories);
            foreach (FileInfo file in folder) {
              if ((file.Attributes & FileAttributes.Directory) != 0) continue;
              files.Add(file);
              maxbytes += file.Length;
            }
          }
          // Copy files
          long bytes = 0;
          foreach (FileInfo file in files) {
            try {
              this.BeginInvoke(OnChange, new object[] { new UIProgress(file.Name, bytes, maxbytes) });
              File.Copy(file.FullName, @"c:\temp\" + file.Name, true);
            }
            catch (Exception ex) {
              UIError err = new UIError(ex, file.FullName);
              this.Invoke(OnError, new object[] { err });
              if (err.result == DialogResult.Cancel) break;
            }
            bytes += file.Length;
          }
        }
        private void Copier_ProgressChanged(UIProgress info) {
          // Update progress
          progressBar1.Value = (int)(100.0 * info.bytes / info.maxbytes);
          label1.Text = "Copying " + info.name;
        }
        private void Copier_Error(UIError err) {
          // Error handler
          string msg = string.Format("Error copying file {0}\n{1}\nClick OK to continue copying files", err.path, err.msg);
          err.result = MessageBox.Show(msg, "Copy error", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation);
        }
        private void Copier_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
          // Operation completed, update UI
          ChangeUI(false);
        }
        private void ChangeUI(bool docopy) {
          label1.Visible = docopy;
          progressBar1.Visible = docopy;
          button1.Text = docopy ? "Cancel" : "Copy";
          label1.Text = "Starting copy...";
          progressBar1.Value = 0;
        }
        private void button1_Click(object sender, EventArgs e) {
          bool docopy = button1.Text == "Copy";
          ChangeUI(docopy);
          if (docopy) mCopier.RunWorkerAsync();
          else mCopier.CancelAsync();
        }
      }
    }
    Saturday, October 21, 2006 5:25 PM
    Moderator
  • Hey nobugz, long time... great code as usual, thanks. Couple of things I really like - 1 progressbar shows overall progress, unlike my codeblock which runs once for each filetype. Copy/Cancel button is a nice touch.

    One question though, will this code break if it does not receive permission to copy sourcefile ? Is there any way to log that error and go on from there ?

    Saturday, October 21, 2006 6:33 PM
  • If there's any problem copying the file, the Copier_Error() method will run.  I display a message box, you can log an error.  Anything goes, the method runs synchronously on the GUI thread.
    Saturday, October 21, 2006 6:59 PM
    Moderator
  • Thanks. Right now this is triggered by buttonclick.

    How can I trigger this code from a folder dragged and dropped onto a form ? If it is a folder then copy *.jpg, *.bmp, *.png" to target path?

    Will this work if the dropped folder contains subfolders ? Will it recursively find named filetypes from subfolders ?

     

    Saturday, October 21, 2006 7:34 PM
  • Add a form field to store the path of the folder and use this field instead of Environment.GetFolderPath().  Check this thread for code to handle drag-and-drop from the shell.
    Saturday, October 21, 2006 7:58 PM
    Moderator
  • Anyone tried Hans' code here on VS2005 using Vista ?

     

    I get the UnauthorisedAccessException

     Access to the path 'C:\Users\Me\Documents\My Videos' is denied.

     

    Any wisdom on how to Vistaproof this code ?

     

    Thanks !

    Tuesday, April 24, 2007 3:27 PM
  • I am unable to convert Han's code to vb.net using online converters.

    CAn someone please help to convert the above block to VB.net ?

    Thanks !
    Monday, April 14, 2008 7:01 PM
  • Imports System.Collections.Generic
    Imports System.ComponentModel
    Imports System.IO

    Public Class Form1

      '--- Class to report progress
      Private Class UIProgress
        Public Sub New(ByVal name_ As String, ByVal bytes_ As Long, ByVal maxbytes_ As Long)
          name = name_ : bytes = bytes_ : maxbytes = maxbytes_
        End Sub
        Public name As String
        Public bytes As Long
        Public maxbytes As Long
      End Class

      '--- Class to report exception
      Private Class UIError
        Public Sub New(ByVal ex As Exception, ByVal path_ As String)
          msg = ex.Message : path = path_ : result = DialogResult.Cancel
        End Sub
        Public msg As String
        Public path As String
        Public result As DialogResult
      End Class

      '--- Members
      Private mCopier 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

      Public Sub New()
        InitializeComponent()
        AddHandler mCopier.DoWork, AddressOf Copier_DoWork
        AddHandler mCopier.RunWorkerCompleted, AddressOf Copier_RunWorkerCompleted
        mCopier.WorkerSupportsCancellation = True
        OnChange = AddressOf Copier_ProgressChanged
        OnError = AddressOf Copier_Error
        ChangeUI(False)
      End Sub

      Private Sub Copier_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
        '--- Create list of files to copy
        Dim theExtensions As String() = {"*.jpg", "*.jpeg", "*.bmp", "*.png", "*.gif"}
        Dim files As New List(Of FileInfo)
        Dim path As String = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
        Dim dir As New DirectoryInfo(path)
        Dim maxbytes As Long = 0
        For Each ext As String In theExtensions
          Dim folder As FileInfo() = dir.GetFiles(ext, SearchOption.AllDirectories)
          For Each file As FileInfo In folder
            If ((file.Attributes And FileAttributes.Directory) <> 0) Then Continue For
            files.Add(file)
            maxbytes += file.Length
          Next
        Next
        '--- Copy files
        Dim bytes As Long = 0
        For Each file As FileInfo In files
          Try
            Me.BeginInvoke(OnChange, New Object() {New UIProgress(file.Name, bytes, maxbytes)})
            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.result = DialogResult.Cancel Then Exit For
          End Try
          bytes += file.Length
        Next
      End Sub

      Private Sub Copier_ProgressChanged(ByVal info As UIProgress)
        '--- Update progress
        ProgressBar1.Value = CInt(100.0 * info.bytes / info.maxbytes)
        Label1.Text = "Copying " + info.name
      End Sub

      Private Sub Copier_Error(ByVal err As UIError)
        '--- Error handler
        Dim msg As String = String.Format("Error copying file {0}\n{1}\nClick OK to continue copying files", Err.path, Err.msg)
        err.result = MessageBox.Show(msg, "Copy error", MessageBoxButtons.OKCancel, MessageBoxIcon.Exclamation)
      End Sub

      Private Sub Copier_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
        '--- Operation completed, update UI
        ChangeUI(False)
      End Sub

      Private Sub ChangeUI(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

      Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim docopy As Boolean = Button1.Text = "Copy"
        ChangeUI(docopy)
        If (docopy) Then mCopier.RunWorkerAsync() Else mCopier.CancelAsync()
      End Sub
    End Class

    Tuesday, April 15, 2008 4:25 AM
    Moderator
  • Thank you very much.

    If I were to put this code in a separate class,
    and have a separate form just to display copy progress + BtnCancel

    how would I call this reusable function from within the project ?
    Tuesday, April 15, 2008 5:02 PM
  • There's a single point where the code gets activated in the sample code, Button1_Click().  Make that a method in your class.  You'd probably also want to pass the source and destination folder names.  And the owner form, the instance of a Form class that acts as the parent of the copy progress form (frmProgess.Show(theOwner)).
    Tuesday, April 15, 2008 10:38 PM
    Moderator
  • Tried this a couple of different ways. I've added the code to a new reusable form with just the progressbar, label & button on it.

    Can't seem to figure the call to pass the source and destination folder names from the parent form to it.
    Wednesday, April 16, 2008 2:48 AM
  • i have a similar situation i'd like to pursue and understand.  let's say from the main app a user requests a series of database operations (three different update queries).  i'd like to be able to start this process in the main app, instantiate and periodically call and update a separate small modal status/progress window as the main app's operations start and finish.  sorta like: 

    Operation 1 has started...done!

    Operation2 has started...done!  etc.

    and when all operations have completed, put the focus on the modal progress form and have the user be able to click an OK button to then close the progress form and return control to the main app.

     

    help!

     

    thanks in advance

     

    Tuesday, September 09, 2008 1:48 PM
  • Very nice example.  Thanks!!

    I'm trying to understand how the progress events are caught during the File.Copy operation which I assume is a blocking method.  I image the magic is somewhere in these two lines:

              this.BeginInvoke(OnChange, new object[] { new UIProgress(file.Name, bytes, maxbytes) });
              File.Copy(file.FullName, @"c:\temp\" + file.Name, true);

    I realize that Copier_DoWork is on a separate thread spawned from the RunWorkerAsync method. But what I'm not following is how File.Copy is able to report back its progress - I don't see the tie between the File.Copy method and the OnChange handler.

    Feels like this is C# 401 and maybe I don't have a right to even know but its driving me crazy that this sample works so well but I can't connect the dots from here to there.

    -- Steve

    Tuesday, June 08, 2010 11:06 PM
  • Ok,  I see now.  I  tried the example and realized its not actually reporting any progress during the File.Copy, just between each file.  The OnProgress bar convolutes this a bit because it tries to move smoothly from point to point.
    Wednesday, June 09, 2010 6:09 AM