简体   繁体   中英

Update progress bar while copying a large file

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

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;
}

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM