Nicholas Aysen
Nicholas Aysen

Reputation: 580

How to properly increment progress bar while background worker is copying files

I am trying to properley implement a background worker with a progress bar in my app. What my app does is check to see if certain files exist on the PC. If they don't, it will copy the files from a server over to the PC. It also checks, if the files do already exist, to see if there are any newer versions of the file for download. If so, it copies them over.

I would like to implement a background worker with progress bar to show the user how far done it is. My work consists of 4 methods that fire to check for and copy the files. And that is where I get confused. All examples I have seen with progress bars and background workers don't usually have methods in the DoWork of the worker.

Here is my code:

private void btnClick_Click(object sender, EventArgs e)
    {
        Form1 frm = new Form1();
        frm.Enabled = false; //to insure the user doesn't close the form until the progress is completed
        worker = new BackgroundWorker();
        worker.WorkerSupportsCancellation = false;
        worker.DoWork += DoWork;
        worker.ProgressChanged += ProgressChanged;
        worker.WorkerReportsProgress = true;
        worker.RunWorkerAsync();
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        while (worker.IsBusy) //I think this is my mistake. Is this in the right place?
        {
            progressBar.Increment(1);
            Application.DoEvents();
        }

    }

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Form1 frm = new Form1();
        if (!(e.Error == null))
        {
            lblDisp.Text = e.Error.Message;
            frm.Enabled = true;
        }
        else
        {
            lblDisp.Text = "Done";
            frm.Enabled = true;
        }
    }//The form will then enable again and display a message once the worker is done

    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar.Value = e.ProgressPercentage;
    }

    protected void DoWork(object sender, DoWorkEventArgs e)
    {
        //And these are the methods that work.
        FindFolders();
        FindFiles();
        ReplaceExeFile();
        CopyDllFiles();
    }

The methods in my app are just your typical copy and overwrite

string folderProg = @"C:\new\prog.exe";
        string debugProg = @"\\192.169.1.1\progfiles\prog.exe";

        var versionDebug = FileVersionInfo.GetVersionInfo(debugProg);
        var versionFolder = FileVersionInfo.GetVersionInfo(folderProg);
        Version verFold = new Version(versionFolder.FileVersion);
        Version verDeb = new Version(versionDebug.FileVersion);

        if (verDeb > verFold)
        {
            File.Copy(debugProg, folderProg, true);
        }

So I know there is nothing wrong with the work. It does copy. My problem is the bar fills up before the actual work starts. And if I move the increment code, it doesn't fill up 100%. The files are copied but the bar would be only 10% full.

So basically, I know I have put the increment code in the wrong place, but I don't know where the code should go.

An ugly way to do this would be to put worker.ReportProgress(25) and increment by 25 in the DoWork and just fill it when completed, while commenting out progressBar.Increment(1) in the while (worker.IsBusy) code. But this is because I know there are 4 methods (4*25). Like so:

private void btnClick_Click(object sender, EventArgs e)
    {
        Form1 frm = new Form1();
        frm.Enabled = false;
        worker = new BackgroundWorker();
        worker.WorkerSupportsCancellation = false;
        worker.DoWork += DoWork;
        worker.ProgressChanged += ProgressChanged;
        worker.WorkerReportsProgress = true;
        worker.RunWorkerAsync();
        worker.RunWorkerCompleted += worker_RunWorkerCompleted;
        while (worker.IsBusy)
        {
            //progressBar.Increment(1);
            Application.DoEvents();
        }

    }

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Form1 frm = new Form1();
        if (!(e.Error == null))
        {
            lblDisp.Text = e.Error.Message;
            frm.Enabled = true;
        }
        else
        {
            lblDisp.Text = "Done";
            frm.Enabled = true;
        }
    }

    private void ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        progressBar.Value = e.ProgressPercentage;
    }

    protected void DoWork(object sender, DoWorkEventArgs e)
    {
        FindFolders();
        worker.ReportProgress(25);
        FindFiles();
        worker.ReportProgress(50);
        ReplaceExeFile();
        worker.ReportProgress(75);
        CopyDllFiles();
        worker.ReportProgress(100);
    }

This way does work. But isn't there another?

EDIT. Just to clarify, my usings:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Diagnostics;

And I'm using .NET Framework 4

Upvotes: 0

Views: 1885

Answers (2)

Dirk
Dirk

Reputation: 10968

I think this is my mistake. Is this in the right place?

Using while (worker.IsBusy) {} defeats the purpose of a BackgroundWorker. As the name implies it should do the work in the background. With a while-loop like that you might as well just do thw work on the UI thread.

You can simply remove this loop, the BackgroundWorker will still do the work and report progress. You might have to make sure that you can't press the button again until the work is done.

All examples I have seen with progress bars and background workers don't usually have methods in the DoWork of the worker.

I'm not sure which examples you checked, but the only place the report the progress from is the DoWork event. That's the only method that actually knows how much it will do and how long it might take.

With regards to the progress bar: in an ideal world you'd have a progress bar that changes its values with time in a linear way. Why with time? Because that's what gives the user a way to see how long he has to wait.

Unfortunately getting a good estimate of the total or remaining time is often impossible. So you'll have to be creative, like you did with 25% after each of the 4 methods. This works, but is not really great because those 25% steps are a bit too large. Other possibilities are to report progress in terms of files or bytes processed.

Upvotes: 2

DMAN
DMAN

Reputation: 471

You have to update the progress from within the DoWork method or the methods you are calling. Simply looping and incrementing the progress does not make sense, you have to do it after certain steps like reading or copying a file.

Upvotes: 1

Related Questions