2

I want to refresh a progess bar in C# WPF.

This question sounds very easy and i should be able to google it. But the solutions I have seen unsatisfy me.

Assuming that i have to run a very long algorithm with e.g. 5 different steps. I don't know exactly how long it will take to calculate the different steps. But i know what i have programmed an i could use a profiler to check how much time the CPU uses for each step (in % of the total time for all steps).

This could be the times e.g.:

Method1() takes 3s
Method2() takes 5s
Method3() takes 1s

This are my approaches:

The "Easy" Approach:

ProgressBar pb = new ProgressBar()
                     {
                         // The total duration of all methods
                         Maximum = 9
                     };

Method1();
// + 3 for 3 seconds
pb.Value += TimeForMethod1;

Method2();
// + 5 for 5 seconds
pb.Value += TimeForMethod2;

Method3();
// + 1 for 1 second
pb.Value += TimeForMethod3;

This is pretty easy. But has a problem. This blocks my UI thread for 9 seconds wich is horrible (because the user could think the programm has crashed).

So it seems obvious to use a thread..


The "Thread" Approach:
This has the problem that I need to dispach every operation on the ProgressBar which could be very slow (for very much updates on the ProgressBar)

I have written a "TaskQueue" for that. I can save all work I want to do in a Queue and a Thread is working all these Task after Run is called and updates the ProgressBar (and a Label) between the Tasks.

I don't want to post all the code of the ThreadQueue, because it is very much and maybe not that good implemented (yet).

This is the important part of the thread method:

foreach (var threadQueueNode in threadQueue)
{
    // Changes e.g. a label displaying what the thread is doing next
    threadQueueNode.PreMainFunction.Invoke();

    // Executes the main task
    this.Result = threadQueueNode.MainFunction.Invoke();

    // Updates the ProgressBar after the work is done.
    threadQueueNode.PostMainFunction.Invoke();
}

PostMainFunction is a Delegate and e.g. this:

PostMainFunction = (Action<int>)((value) => this.Dispatcher.Invoke(() => this.ProgressBarStatus.Value += value));

Wich is the professional way to update a ProgessBar for a problem like mine?
I'd be happy about a discussion.

Thanks for your help and your time!

Jens
  • 1,822
  • 2
  • 11
  • 33

2 Answers2

3

BackgroundWorker is clean and legible:

var bw = new BackgroundWorker();

bw.DoWork += (s, e) =>
{
    var worker = s as BackgroundWorker;
    Method1();
    worker.ReportProgress(30);
    Method2();
    worker.ReportProgress(80);
    Method3();
    worker.ReportProgress(100);
};

bw.ProgressChanged += (s, e) =>
{
    pb.Value += e.ProgressPercentage;
};

bw.RunWorkerCompleted += (s, e) => 
{ 
};

bw.RunWorkerAsync();
Reza ArabQaeni
  • 4,725
  • 24
  • 43
  • can you explain me the difference between a background worker and a thread? seems to me to be the same with the difference that the BG has some events like "WorkCompleded" and other stuff. – Jens Jun 15 '15 at 05:19
  • @JensHorstmann you right difference is managed thread and proper events, as i mentioned it's clean and legible. – Reza ArabQaeni Jun 15 '15 at 06:25
0

This has the problem that I need to dispach every operation on the ProgressBar which could be very slow

The solution is to precompute the changes to the progressbar and only invoke the UI thread and update the graphics when needed.

For example, you have 100.000 operations. You do not need to update the progress bar 100.000 times. The user will not notice the difference anyway. Update once for each percent (1000 records in this case) so the user will be notified on each percent. Updating the UI every few seconds is fine.

nvoigt
  • 61,531
  • 23
  • 73
  • 116
  • Well, i have thought about that too. But is my "Thread Approach" the right way? or is there a better one? – Jens Jun 14 '15 at 09:45