Only 1 of 2 progress bars gets updated in BackgroundWorker - c#

I have a simple form with 2 progressbars and 1 backgroundworker on it. I have 2 loops (one within the other) and I'd like to report back the progress of each loop once incremented. Here's the code I have:
private void buttonStart_Click(object sender, EventArgs e)
{
workerCustomers.RunWorkerAsync();
}
private void workerCustomers_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
progressBar2.Value = (int)e.UserState;
}
private void workerCustomers_DoWork(object sender, DoWorkEventArgs e)
{
for (int customer = 0; customer < 50; customer++)
{
int customerPercentage = ++customer * 100 / 50;
workerCustomers.ReportProgress(customerPercentage, 0);
for (int location = 0; location < 500; location++)
{
int locationPercentage = ++location * 100 / 500;
workerCustomers.ReportProgress(customerPercentage, locationPercentage);
}
workerCustomers.ReportProgress(customerPercentage, 0);
}
}
When the program runs, progressbar1 gets updated just fine, but progressbar2 never moves. If I run it through the debugger, I can see the value of progressbar2 being changed, there's just no change graphically. Any ideas?

Yes, the 2nd progress bar will fail to update if you have Aero enabled on your desktop. Which provides the animated progress bar, the one that's green by default with the traveling highlight note.
This progress bar style is special, it interpolates intermediate values and always looks smooth, even if you assign course values to the Value property. In other words, you can assign 10, 20, 30, etcetera but the bar doesn't jump. It smoothly increases the bar length.
This has side-effects. To make this animation work, the progress bar must lag. In other words, it is always showing a value that's behind the programmed value. And some time must pass for it to smoothly increase the bar length to the programmed value.
One more detail you need to know: this animation does not happen when you decrease the Value property, that immediately jumps the bar length.
So the problem is that you are updating the Value property at a very high rate, much faster than the bar can interpolate. It can never catch up. Except when you restart at 0, that immediately jumps the bar. The rate is so high that the animation can never make it to 1.
You can see this by inserting this statement inside the inner loop:
System.Threading.Thread.Sleep(1);
Play with the argument value, you may need to increase it to 16 to see any difference. The higher you make it, the further the bar makes it by interpolation.
This is of course not a real problem, this happens because your worker isn't doing any real work. As soon as you start writing the real code this BGW is supposed to do then you'll also decrease the rate at which you call ProgressChanged.

The problem lies in the fact that your inner loop is so tight that the gui updates are posting too frequently for the gui to accurately respond to them. The solution is to add your real work to the inner loop, so it takes longer or if you want to simulate the GUI, you can just add a Thread.Sleep(100) to simulate the work taking some time.
Also ensure that your progress bar maximums are set to 100, as that is the maximum value you are returning for each.

Your main problem comes from a too fast inner loop which means you cant actually see the progress since the updates are too fast for the progressbar to handle.
Your line of code:
location * 100 / 500
will equal to 100 when you finish the inner loop, so having the progressbar max value set to 500 does not relate well to that number. It means your progressbar will never reach max value.

Related

Picture box moves with timer, but it lags behind?

I'm not sure how to explain this. I have the code set up so when the timer ticks, it checks to see if the picture box is at a certain location. If it isn't, then it should move a little bit closer. It works in moving, but each time it moves, it generates a white field behind it, and it grows. I have the tick set to 750, so each time it ticks, the picture moves farther than it should, and each time it goes farther than the previous time. Here's the code.
private void ZombieTimer1_Tick(object sender, EventArgs e)
{
ZombieTimer1.Tick += new System.EventHandler(ZombieTimer1_Tick);
enemyNPC1.Show();
ZombieTimer1.Start();
if (enemyNPC1.Location.X < 280)
{
enemyNPC1.Left = enemyNPC1.Left + 1;
ZombieTimer1.Stop();
ZombieTimer1.Start();
}
}
Move the declaration of the event handler to the constructor for your window.
At the moment you get another event handler every time the event fires, which causes the event to fire twice the second time, and four times the next time, etc.

Thread.Sleep and Painting UI

I have this stoplight program that models a strange intersection near where I live. I am pretty sure I have the logic down. I have functions that represent every possible state of the lights, including yellows. The problem is that none of my yellow lights are painting. Five seconds after a car pulls up to a light and no other cars have passed through green lights, yellow lights for appropriate lights are supposed to turn on for three seconds, and then turn green. For some reason when I step through my program, no yellow lights are painting. Here is my code:
private void WaitThreeSecs()
{
//somehow I think this is to blame
int threeSeconds = 3000;
Thread.Sleep(threeSeconds);
}
private void FromStateOneToTwo()
{
//Stop Countdown Timers
fiveSec.Stop();
thirtySec.Stop();
//Indicate Current State
CurrentState = 2;
//Paint lights
BottomGreen.Fill = gray;
BottomYellow.Fill = yellow; // this never happens
BottomRed.Fill = gray;
//Wait 3 secs
WaitThreeSecs();
btnbottom.Content = "Car Stops";
//Call function that changes the state for other lights to be green
StateTwo();
}
FromStateOneToTwo() is called after the five seconds, but the light is never yellow. I have no idea why this doesn't happen. The light waits 8 seconds from when a button is pressed to when the appropriate light goes straight from red to green, meaning that my 3 second timer is working, but my painting the light is not. (I paint in other function exactly like i do here, and it works fine)
I can show more/all of my code if needed.
You aren't getting a cross-thread InvalidOperation exception, so I'll assume that the provided function runs on the UI thread. This will happen if you invoke it off of a button-click, or the elapsed event of a DispatcherTimer.
By sleeping the UI thread, you don't give it time to paint the screen. It switches colors, then back, without a paint method ever running.
Your best solution is to use a standard Timer so you aren't on the UI thread, then marshal your changes to the UI thread with Dispatcher.BeginInvoke (WPF) or Control.Invoke(WinForms). You can also eliminate Thread.Sleep and make more timers to trigger the next changes.
Use Application.DoEvents right before the WaitThreeSecs() tot force a screen refresh

How can i use progress bar in non length specific tasks or parallel loop?

I need progress bar in windows form application but my process is not length specific so I can't maximum value to progressbar.
I must increment progress bar value using LongProcess class Run method elapsed time.
void start_click()
{
button1.Enabled=false;
Task.Factory.StartNew(()=>{
Parallel.Foreach(files,f=>{
LongProcess process =new LongProcess(); // Long-running process (is not my control)
process.Run(f);
//progressBarValueIncrement();
});
}).ContinueWith(t=>{
// completed
BeginInvoke(new Action(() => button1.Enabled=true));
});
}
How can i implement this?
you could increment the progress bar with some percentage of the remaining unfilled value. This would mean that the progress bar never filled up though and would get fuller more and more slowly.
So in the first increment you fill the progress bar 50%, then next increment you fill 50% of the remainder, so 25% for a total of 75%, then 50% of remaining 25%, total 87.5% etc etc.
you may want to stage it so that the %age you used was based on how full the progress bar was, so start with 5% at the beginning so it doesn't fill up too quickly at the start, then move to higher percentages so you can see the bar moving still near the end...
If you don't know how long something is going to take though, is a progress bar appropriate? Maybe you would be better with something that showed progress was happening, but not how much has been completed...

WPF ProgressBar loses its glow when it updates too quickly - bug?

Ok, so I found some rather weird behaviour while messing around with the WPF ProgressBar control. This control is located in a column of a ListView control and the general situation differs little from this question & answer in its essence.
I bind Progressbar to a class by means of several properties (Min, Max, Value), all OneWay Bindings obviously. This other class is updated from another thread and regularly uses the INotifyPropertyChanged interface to let the ProgressBar know the status is progressing. And this all works just great!
But here is where it gets odd. My ProgressBar loses its glow.. right upto the moment it reaches the Max (=100%) value. Then it suddenly starts pulsing its white glowy stuff all over the green bar, and this is very annoying. I am showing progress with a reason, and the lack of a pulse is actually pretty distracting once you start to notice it not being there.
Thus, I set off to debug. I found that with Thread.Sleep(1000) in my threads processing, it still hid the glow, but if I bump it to Thread.Sleep(1500) the glow comes back at all times with a crazy vigour. After that, I tried translating my progress units to smaller numbers so the integer values would take longer to change. Min 0, Max 100 still has the lack of the glow. Min 0, Max 10 had the glow come back in its full vigor. In all cases, it is the same amount of work and time spent to reach 100%, but it is a very visible binary YES/NO effect with regards to the glow showing. The only thing I have not tested is whether it also happens when the ProgressBar is not placed inside of this ListView control.
I know myself well enough that I can't make sense of the deep WPF innards of the (XAML involved with the) ProgressBar control. So I was hoping anyone here knows whether this is a known bug, something they stumbled into, or something they might even know how to work around/fix.
My machine runs Windows 7, and I'm developing in VS2010 targeting .NET Framework 4 Client Profile.
I would take a guess and say that you lose the glow because you are updating your progress bar to often. Every time you set a new value the progress bar restarts its glowing animation (I think - I haven't tested this, I'm writing off the top of my head).
It seems that you have perhaps thought of the same thing and tried to work around it, but I'm not sure you have fully exhausted all possibilities:
Try creating a check that if (progressbar.Value == newValue) don't do progressbar.Value = newValue;
Progressbar should be using Decimals for Min, Max, Value. Make sure you don't do updates for every decimal point, eg. - 10,1; 10,2; 10,3; etc... (use progressbar.Value = (int)newValue;)
Try setting the progressbar value in bigger increments, instead of increment = 1, use increment = 10;
You could try taking a progressbar outside of ListView, maybe there is a rendering bug with progressbar being inside it.
PS! If you update your progressbar very rapidly, then it is OK for the glow animation not to run. Remember that the glow animiation's purpose is only to show that the application is still running (machine hasn't frozen), despite the fact that the progress(bar) hasn't moved.
If the progress is moving quickly, then that on its own is a visual effect for the user, so there is no need to have the glow animation at that moment...

Form opacity animation in C# with a BackgroundWorker

With the help of the BackgroundWorker, I created an opacity animation for some form.
There's only one tiny issue with this approach but I can't understand where is the problem. The animation speed is configurable and even if the speed value is very high, sometimes the animations is very, very slow, for some odd reason...
The "slow animation" I'm talking about it's not stutter, the animation is actually very smooth, it just takes more time to perform the whole animation (from 0% to 100%, or vice-verse). This only happens from time to time. It seems (not sure) that it happens when the computer is doing some other, somewhat intensive, background action.
I need to fix that of course but I also would like to know if there's anyway you would improve this code or if you would do it differently and/or better.
Here's my code:
private const int TOGGLE_EFFECT_SPEED = 10;
private void blendWorker_DoWork(object sender, DoWorkEventArgs e) {
bool blendIn = (bool)e.Argument;
// Loop through all opacity values
for(double value = 1; value <= 100; value += 1) {
// Report the current progress on the worker
blendWorker.ReportProgress(0, blendIn ? value : 100 - value);
// Suspends the current thread by the specified blend speed
System.Threading.Thread.Sleep(11 - TOGGLE_EFFECT_SPEED);
}
// Set the worker result as the inverse tag value
e.Result = !blendIn;
}
private void blendWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) {
double opValue = (double)e.UserState;
// Show and repaint the whole main notes window?
if(opValue == 1.0) {
Show();
Invalidate(true);
}
// Set the main notes window opacity value
Opacity = (double)e.UserState / 100;
}
private void blendWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
bool tagFlag = (bool)e.Result;
// Hide the main notes window?
if(tagFlag) {
Hide();
}
// Set the main notes window tag value
Tag = tagFlag;
}
/*
THE FOLLOWING METHOD IS PART OF A DIFFERENT CLASS.
ACTUALLY, IT'S THE "PROGRAM" CLASS WHERE MAIN()
IS LOCATED. THIS METHOD IS CALLED TO SHOW/HIDE
THE MAIN APPLICATION FORM WITH AN OPACITY ANIMATION
*/
internal static void ToggleNotesWindow() {
// Get the tag value converted to boolean type
bool tagFlag = Convert.ToBoolean(NotesWindow.Tag, CultureInfo.InvariantCulture);
// Bring the main notes window to front?
if(tagFlag) Program.NotesWindow.BringToFront();
// Run the blend effect if it's not already running
if(!NotesWindow.blendWorker.IsBusy) {
NotesWindow.blendWorker.RunWorkerAsync(tagFlag);
}
// Activate and focus the main notes window?
if(tagFlag) Program.NotesWindow.Activate();
}
Each time you change the opacity of the form, Windows has to redraw all the windows below it, the window itself and then apply opacity (Vista does this much faster and buffered). Since you're stepping through each opacity state from 1...100 this process has to complete 100 times. Sometimes redrawing your window or a window below it will be slow.
The Thread.Sleep method with a value > 0 will sleep from 0...~10ms no matter what value you pass. The thread scheduler timer resolution on Windows is approx 10ms (Again, Vista and other OS change and optimize so it's not exact) so you can't schedule a time slice smaller than that. 100x10ms + the time to actually render might take 2 whole seconds to fade in/out.
The way to speed it up is to reduce the number of re-draws. Rather than stepping +1 for opacity, step +5, +10, etc. That reduces the total number of re-draws required and would result in a form fading in 100ms instead.
Overall I don't see much that would warrant a change at least at first glance. If you are seeing some performance bottlenecks you might try having a look at Ants Profiler or a similar code profiling tool to see if you can pinpoint any slow sections.

Categories