A *generic* progress bar dialog with BackgroundWorker?

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

Hi!

I'm having some trouble with BackgroundWorker objects and making some
"heavy" work that is *not* supposed to be in the progress bar dialog itself.
I basically want to do something like this:

ProgressForm form = new ProgressForm();
form.Show();
// ... Do heavy work here ...
form.UpdateProgress(label, progressValue);
// ... More heavy work ...
// Done!
form.Close();

And by doing this, have ProgressForm coded so it dosen't lock up the UI.
Would this be possible? My problem is that doing the work in the
ProgressForm's DoWork event handler used by the BackgroundWorker is not an
option because that would make ProgressForm work-specific, which I'd really
dislike to see. I want a reusable progress dialog, not make the dialog
specific depending on the heavy work I wish to do.

So far most examples I've seen on the web seem to be about work-specific
progress dialogs where the "heavy work" is in the DoWork function itself. :(

So I've played around with constructing the ProgressForm with an argument
being a delegate to the calling class' "heavy work" function, that will be
called back by the DoWork() handler. The "heavy work" function would have a
ProgressInfo object as return value, which would contain the progress value
and label to use after it returns from being called in DoWork().

That seemed like a bright idea until I saw that I then need the work in its
own function, which makes me need to supply it with its set of arguments
(like an array of objects to process), and the only option I can then see is
the make such arrays and everything related to the "heavy work" fields in the
calling class. :-/

Is there a "pretty" way out this by keeping things reasonably simple and not
hardwiring my heavy work loop into/inside the progress bar dialog class
itself?
 
OK, I'll reply to myself with the solution I have working now. The only
"imperfect" thing for what I'd like now is that I need to pull out the
variables from the function delegate that'll be called from the worker
function, because I want to generalize the worker function.

Here's my code if anyone's interested...

First, the generic ProgressForm:

(lblWork is a label telling what it's currently doing, like which file it's
working on in a series... I have placed a "worker" object of type
BackgroundWorker in design mode, set to WorkerReportsProgress = true and
WorkerSupportsCancellation = true)

----------------

using System.Windows.Forms;

namespace MyNamespace // set this to whatever you use
{
public partial class ProgressForm : Form
{
// The callback function for the BackgroundWorker's DoWork()
public delegate ProgressInfo HeavyWorkFunction();

private readonly int max;
private readonly HeavyWorkFunction workFn;

public ProgressForm(string title, int max, HeavyWorkFunction workFn)
{
InitializeComponent();

this.Text = title;
this.max = max;
this.workFn = workFn;

worker.RunWorkerAsync();
}

private void worker_DoWork(object sender,
System.ComponentModel.DoWorkEventArgs e)
{
ProgressInfo pi;
while ((pi = workFn()).progress < max)
worker.ReportProgress(100 * pi.progress / max, pi.label);
}

private void worker_ProgressChanged(object sender,
System.ComponentModel.ProgressChangedEventArgs e)
{
progress.Value = e.ProgressPercentage;
lblWork.Text = (string)e.UserState;
}

private void worker_RunWorkerCompleted(object sender,
System.ComponentModel.RunWorkerCompletedEventArgs e)
{
Close();
}
}

public class ProgressInfo
{
public string label;
public int progress;
}
}

----------------

Then, a sample usage... For example, synchronizing a bunch of files with a
mobile device.

----------------

public void Synchronize()
{
// At this point, the variable totalSize contains the total
number of bytes to transfer
pi = new ProgressInfo();
ProgressForm form = new ProgressForm("Synchronizing...",
totalSize, SyncWorker);
form.ShowDialog();
}

/* Field variables... This is the annoying part -- need more fields the more
complex and dependant the SyncWorker() function is on the rest of the class,
in order to keep ProgressForm a 100% "generic" class. */

private int currentSize, totalSize;
private int nextPos;
private List<FileInfo> files;
private ProgressInfo pi;

public ProgressInfo SyncWorker()
{
FileInfo fileInfo = files[nextPos];

// This takes some time...
device.CopyFileToDevice(fileInfo.FullName,
destDir + @"\" + fileInfo.Name);

currentSize += (int)fileInfo.Length;

pi.label = string.Format("Copying {0} (transferred {1} bytes) ...",
fileInfo.Name, currentSize);
pi.progress += currentSize;

nextPos++;

return pi;
}
 
Oops, forgot the Cancel handler in the ProgressForm... But it's simple
enough. ;)

private void btnCancel_Click(object sender, System.EventArgs e)
{
worker.CancelAsync();
}
 
Back
Top