2

I'm looking for a way to update my progress bar while copying a file from one location to another.

I'm doing the copy in a BackgroundWorker and have the progress bar updating in the background as well. I've tried using the file.length to get the file size and use that to work the percentage and update the bar that way but to no joy.

I'm attaching the code and any help would be greatly appreciated, Thank you.

namespace Copier

{ public partial class Form1 : Form { public Form1() { InitializeComponent(); }

    // Declare for use in all methods
    public string copyFrom;
    public string copyTo;

    private void btnCopyFrom_Click(object sender, EventArgs e)
    {
        // uses a openFileDialog, openFD, to chose the file to copy
        copyFrom = "";

        openFD.InitialDirectory = @"C:\Documents and Settings\user\My Documents";
        openFD.FileName = "";

        //openFD.ShowDialog();

        if (openFD.ShowDialog() == DialogResult.Cancel)
        {
            MessageBox.Show("cancel button clicked");
        }

        else
        {
            // sets copyFrom = to the file chosen from the openFD
            copyFrom = openFD.FileName;
            // shows it in a textbox
            txtCopyFrom.Text = copyFrom;
        }
    }

    private void btnCopyTo_Click(object sender, EventArgs e)
    {
        //uses folderBrowserDialog, folderBD, to chose the folder to copy to
        copyTo = "";

        this.folderBD.RootFolder = System.Environment.SpecialFolder.MyComputer;
        this.folderBD.ShowNewFolderButton = false;
        //folderBD.ShowDialog();
        //DialogResult result = this.folderBD.ShowDialog();

        if (folderBD.ShowDialog() == DialogResult.Cancel)
        {
            MessageBox.Show("cancel button clicked");
        }
        else
        {
            // sets copyTo = to the folder chosen from folderBD
            copyTo = this.folderBD.SelectedPath;
            //shows it in a textbox.
            txtCopyTo.Text = copyTo;
        }
    }

    private void btnCopy_Click(object sender, EventArgs e)
    {
        copyBGW.RunWorkerAsync();
    }

    private void btnCancel_Click(object sender, EventArgs e)
    {
        Application.Exit();
    }

    //=================================================================
    //                      BackGroundWorkers
    //=================================================================

    private void copyBGW_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            // copies file
            string destinatationPath = Path.Combine(copyTo, Path.GetFileName(copyFrom));
            File.Copy(copyFrom, destinatationPath);
            MessageBox.Show("File Copied");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

Or could someone show me a way to just make the progress bar go by its self so it shows that the form hasn't frozen?

Have cleaned up the code

Thanks for the input so far

ELSheepO
  • 305
  • 3
  • 9
  • 26

3 Answers3

3

I think it would be easiest to just call CopyFileEx which allows you to specify a progress handler so you get updates from the OS as the file is copied. Here is the sample code copied from the pinvoke.net page for CopyFileEx :

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CopyFileEx(string lpExistingFileName, string lpNewFileName,
   CopyProgressRoutine lpProgressRoutine, IntPtr lpData, ref Int32 pbCancel,
   CopyFileFlags dwCopyFlags);

delegate CopyProgressResult CopyProgressRoutine(
long TotalFileSize,
long TotalBytesTransferred,
long StreamSize,
long StreamBytesTransferred,
uint dwStreamNumber,
CopyProgressCallbackReason dwCallbackReason,
IntPtr hSourceFile,
IntPtr hDestinationFile,
IntPtr lpData);

int pbCancel;

enum CopyProgressResult : uint
{
    PROGRESS_CONTINUE = 0,
    PROGRESS_CANCEL = 1,
    PROGRESS_STOP = 2,
    PROGRESS_QUIET = 3
}

enum CopyProgressCallbackReason : uint
{
    CALLBACK_CHUNK_FINISHED = 0x00000000,
    CALLBACK_STREAM_SWITCH = 0x00000001
}

[Flags]
enum CopyFileFlags : uint
{
    COPY_FILE_FAIL_IF_EXISTS = 0x00000001,
    COPY_FILE_RESTARTABLE = 0x00000002,
    COPY_FILE_OPEN_SOURCE_FOR_WRITE = 0x00000004,
    COPY_FILE_ALLOW_DECRYPTED_DESTINATION = 0x00000008
}

private void XCopy(string oldFile, string newFile)
{
    CopyFileEx(oldFile, newFile, new CopyProgressRoutine(this.CopyProgressHandler), IntPtr.Zero, ref pbCancel, CopyFileFlags.COPY_FILE_RESTARTABLE);
}

private CopyProgressResult CopyProgressHandler(long total, long transferred, long streamSize, long StreamByteTrans, uint dwStreamNumber,CopyProgressCallbackReason reason, IntPtr hSourceFile, IntPtr hDestinationFile, IntPtr lpData)
{
    return CopyProgressResult.PROGRESS_CONTINUE;
}
Chris Dunaway
  • 10,974
  • 4
  • 36
  • 48
  • Technically this does still block the calling thread, but sure is a nice way to get progress from file copying, and canceling, and "Restart" behavior (which may be undesirable so be careful). But yeah the call to FileCopyEX does block the calling thread until the copy is finished or cancelled. Doesn't preclude you from updating a progress bar on the form however. – TCC Sep 25 '13 at 21:30
2

This can't work on multiple levels. First of all, the background worker needs to be set to "report changes" via WorkerReportsProgress, but this flag doesn't mean he can do that automatically, of course that won't work. For that the Worker provides the method ReportProgress you need to call that method to show the current progress. And this reveals the final flaw of your approach. The File.Copy method is blocking but your worker needs time to call the ReportProgress method. So you need to find a way to copy your file asynchronously. This question might help and of course Dave Bishs comment is a very good reference for async file copy.

Community
  • 1
  • 1
dowhilefor
  • 10,971
  • 3
  • 28
  • 45
  • 1
    Here is an other example of how to copy a file and report the progress: http://stackoverflow.com/a/6055385/519244, the DoWork eventhandler of the BackgroundWorker could look like the Copy method here, replacing the OnProgressChanged call by a call to the ReportProgress method of the BackgroundWorker. – Renaud Dumont May 09 '12 at 14:24
  • @Renaud Dumont i'm not sure if creating your own copy method using streams is that wise. I would think that the actual copy method is much more efficient for this kind of io operation, but i have no reference for that. But yeah, if there is some kind of optimization with the actual copy, it is possible that thats why there is no asynchronous version of it. – dowhilefor May 09 '12 at 14:26
  • First off thanks for the input. I'm not the greates programmer in the world, only started learned c# a week or two ago. I know the first code was full of little silly errors, I put them in to try and help show what i was trying to do. I've gone for using the 'progressBar1.Style = ProgressBarStyle.Marquee;' to show that the program hasn't froze up and a label that changes when the file is copying and copied. Once again, thanks for the input and I know my solution is highly dumbed down from what I asked, but I just can't get my head around async fully. Thanks – ELSheepO May 10 '12 at 09:58
0

My suggestion would be to move the contents of btnCopyTo_Click and btnCopyFrom_Click to separate DoWork background workers for each one and then use btnCopyTo_Click and btnCopyFrom_Click to trigger the background workers. The background workers can be used to report progress so you won't even be able to begin without doing that.

As for when to actually update the progress bar, I recommend determining, each time, the size of the files you're copying.

If you then split the files into blocks of a certain size and then use a loop to copy one block at a time then you should be able to increment the progress bar by a certain amount for however many cycles of the loop.

Alternatively find a way to have an asynchronous thread which loops the progress bar continuously from 0 to 100 whilst the copying is taking place. It's a pretty basic solution but it at least lets the user know that something's happenning.

GeorgePotter
  • 889
  • 1
  • 10
  • 18