0

I am executing a DLL using backgroundWorker1 which updates a variable i by reference. To update a progress bar using i, I use the following code. I also want to show the percentage as text. The problem is that the text (NOT the progress bar) flickers a lot. How can I reduce/remove this flicker? Increasing sleep duration is not an option.

BackgroundWorker backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += (s, args) =>
{
   Mydll.MyCFunction(ref i);
};
backgroundWorker1.RunWorkerAsync();

while (backgroundWorker1.IsBusy)
{
    backgroundWorker1.ReportProgress(i * 100);               
    backgroundWorker1.ProgressChanged += (s, e) =>
    {
       progressBar1.Refresh();
       progressBar1.Value = e.ProgressPercentage;
       progressBar1.CreateGraphics().DrawString(e.ProgressPercentage.ToString() + "%",
            SystemFonts.DefaultFont, Brushes.Black,
            new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
    };
    Application.DoEvents();
    System.Threading.Thread.Sleep(200);
}

Thanks.

Prometheus
  • 153
  • 3
  • 12

4 Answers4

1

Your code will be subscribing to the ProgressChanged event many, many times over. You should only need to subscribe one event to it. Having all these events will mean that for every progress changed, an increasing number of refreshes on the progress bar will occur.

I'd suggest creating a separate event handler for the progress changed event and making sure you only subscribe once.

So, trying to keep your design:

Subscribe event handler to ProgressChanged (Event Handler declared elsewhere)

Loop {

ReportProgress

DoEvents

Sleep }

As a side note, why are you sleeping the thread?

Jamie Kelly
  • 309
  • 3
  • 15
  • Can't really tell why (since we don't have the full source) but he probably sleeps his thread otherwise he will slow down his computer. (Or does Application.DoEvents() blocks, I didn't think so.) – joell Apr 08 '13 at 10:20
  • I sleep because the DLL takes a while to perform iterations and update `i`. – Prometheus Apr 08 '13 at 10:27
1

If graphics flicker you should enable double buffering.

DoubleBuffered = true

Or

SetStyle(ControlStyles.OptimizedDoubleBuffer, true);

For more information: http://msdn.microsoft.com/en-us/library/3t7htc9c.aspx

joell
  • 396
  • 6
  • 17
  • Not that I do care about rep or something, but will the person who downvoted at least explain why so I can learn from my faults. – joell Apr 08 '13 at 12:18
0

Your code has multiple bugs. One of them, generally speaking is calling Application.DoEvents. That may have been useful in VB6 15 years ago, but that's technology stone age. You are also attaching the same handler to the event multiple times, which means after 100 loops your drawing will be called 100 times per percent.

With so much wrong it's hard to suggest a simple fix. Have a look here for two simple ways to make this work. Try to understand what happends, in which order and why.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
0

Firstly, I'd remove the line Application.DoEvents(); (excellent post on do events), this is usually indicative of bad multithreaded design. I'd also remove the Thread.Sleep call personally as well.

Secondly, you should be subscribing to ProgressChanged OUTSIDE of the worker loop. For example:

public Form1()
{
    bgWorker.ProgressChanged += bgWorker_ProgressChanged;
}

private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressBar.Value = e.ProgressPercentage;
    progressBar1.CreateGraphics().DrawString(e.ProgressPercentage.ToString() + "%",
            SystemFonts.DefaultFont, Brushes.Black,
            new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
}
Community
  • 1
  • 1
DGibbs
  • 14,316
  • 7
  • 44
  • 83
  • When I keep `ProgressChanged` outside the `while (backgroundWorker1.IsBusy)` loop, the progrss bar does not update. – Prometheus Apr 08 '13 at 10:13
  • Remove the `while(backgroundWorker1.IsBusy)` block too. Just call report progress after one pass through the method. Since you haven't posted the full do_work method, I can't say for certain though. – DGibbs Apr 08 '13 at 10:15
  • Can you post the full method that all of that code resides in? You really should be setting up the bg worker in one place, form1() or init for example, and then doing the work in another. I would also just bind to an event handler rather than a delegate for your do work. There could be a lot more wrong with your code by the looks of things – DGibbs Apr 08 '13 at 10:20
  • Removing `while(backgroundWorker1.IsBusy)` does not help. the progress bar still doesn't update. – Prometheus Apr 08 '13 at 10:21
  • The reason I use a labmda expression is so that I can update `i` from the DLL. The DLL is built from C code, whose source is not available. – Prometheus Apr 08 '13 at 10:23
  • Put this `backgroundWorker1.ReportProgress(i * 100); ` inside of your dowork method after the function call. Kinda hard to help you with so little info available... – DGibbs Apr 08 '13 at 10:26
  • The loop that updates `i` is inside `Mydll.MyCFunction`. That is why I pass `i` by reference. If I put `ReportProgress` in `dowork`, wouldn't the call to `Mydll.MyCFunction` block its execution? – Prometheus Apr 08 '13 at 10:31
  • Why would it block it inside of do work but not inside the `while(backgroundWorker.IsBusy)` etc...? They are running in the same thread. – DGibbs Apr 08 '13 at 10:40
  • Since I call `RunWorkerAsync()` before `while(backgroundWorker.IsBusy)`, I am assuming the while loop is on a different thread. – Prometheus Apr 08 '13 at 10:44
  • I tried your original solution again, but this time with `Application.DoEvents();` and the progress bar now updates. But the flicker still exists. – Prometheus Apr 08 '13 at 11:00