I have this code:
for(int k = 0; k<11; k++)
{
pBar.Maximum = 10;
pBar.Value = k;
if (pBar.Maximum == k)
pBar.Value = 0;
}
However, the problem is, the progressbar gets reset when it is about 60% full. How can I ensure that the progressbar will fill all the way before being reset?
First: there is no any reason to assign pBar.Maximum on every interarion.
Just do:
pBar.Maximum = 10;
for(int k = 0; k<11; k++)
{
pBar.Value = k;
if (pBar.Maximum == k)
pBar.Value = 0;
}
Second: your code result in blocking iteration. There is no way it could ever behave correctly. Use multi-threading and change the progress value based on some event,tick whatever, not in loop, as it's done here.
If you switch to Classic mode, this glitch will be gone. The progress bar will appear fully drawn before being reset.
This is because in Classic mode, the painting operation is synchronous and completes before the Value setter returns, but in the themed mode, there is some sort of animation played when you increase the value, and that takes some time to play.
On contrary, when you decrease the value, there is no animation; the progress bar is shrinked immediately.
This is why it appears only about 60% full: you decrease the value (which completes immediately) before the progress bar has time to draw the animation for the last several increments.
pBar.Maximum = 10;
int count = 0;
Timer timer = new Timer();
timer.Interval = 1000;
timer.Tick += (source, e) =>
{
pBar.Value = count;
if (pBar.Maximum == count)
{
pBar.Value = 0;
timer.Stop();
}
count++;
}
timer.Start();
Your problem is that you're using a loop. You need to use a timer so that the program has time to both perform the checks/assignments and update the screen.
The code replaces the for loop with a timer that calls the body of the loop. Since a timer does not have an index variable, it is initialized outside of the timer (count) and updated with each tick.
I finally found a solution to this problem, and wrote about it here. The idea was from THIS SO question.
progressBar1.Value = e.ProgressPercentage;
if (e.ProgressPercentage != 0)
progressBar1.Value = e.ProgressPercentage - 1;
progressBar1.Value = e.ProgressPercentage;
if (progressBar1.Maximum == e.ProgressPercentage)
progressBar1.Value = 0;
Related
Let's say I'm making a game where I want Timers or Stopwatches to slow down in response to slowmo effects. How do I go about doing that? Looking at the API docs, there doesn't seem to be any way to scale the rate that they do their thing, so I was wondering if there's some other trick I can use to get the same effect.
You can set the interval to part of its original value and use some variable to keep track of the elapsed time
Like this:
int counter = 0;
int scale = 2; //scaling factor
int originalInterval = 1000;
Timer timer = new Timer();
timer.Interval = originalInterval / scale;
timer.Tick += (sender, e) => {
counter++;
if (counter >= scale) {
// perform the needed action here
counter = 0; // reset the counter
}
};
If you want to use a stopwatch, then like this:
Stopwatch stopwatch = new Stopwatch();
int scale = 2;
stopwatch.Start();
while (true) {
//Scale the elapsed time and by the factor
double elapsed = stopwatch.Elapsed.TotalMilliseconds / scale;
//When the scaled elapsed time exceeds a threshold
if (elapsed > threshold) {
// perform the needed action here
break;
}
}
I need to write a code which moves a picture1 to the top of the screen after a click of a button. After pictire1 reaches the top 20 pixels of the screen must become invisible and make picture2 visible. This is my wrong code:
private void button1_Click(object sender, EventArgs e)
{
int b = pictureBox1.Top;
for (int i = b; i < 20; i--)
{
pictureBox1.Top = i;
System.Threading.Thread.Sleep(20);
}
if (pictureBox1.Top < 20)
{
pictureBox1.Visible = false;
pictureBox2.Visible = true;
}
}
Any ideas how can it be fixed?
This seems wrong:
for (int i = b; i < 20; i--)
{
pictureBox1.Top = i;
System.Threading.Thread.Sleep(20);
}
This would loop while i < 20. From your code, however, one can see that if Top == 20 is reached, you'd like to show another picture. So I guess that should be i >= 20 and the if should be Top <= 20.
Also, you are not refreshing your display. Due to the loop and the Sleep, there will be no UI updates.
Add this, as Cobra_Fast suggests before the Sleep:
this.Invalidate();
this.Refresh();
To sum it up, the following should work (I've also slightly modified the code to make it clearer):
private void button1_Click(object sender, EventArgs e)
{
while (pictureBox1.Top >= 20)
{
pictureBox1.Top = pictureBox1.Top - 1;
this.Invalidate();
this.Refresh();
System.Threading.Thread.Sleep(20);
}
// Here you KNOW that the picture box is at y-position 20, so there's not need
// for the IF
pictureBox1.Visible = false;
pictureBox2.Visible = true;
}
The problem with the above code is that it blocks the UI. To keep it responsive, I'd use a timer as follows:
private void button1_Click(object sender, EventArgs e)
{
// Create a threaded timer
System.Timers.Timer animationTimer = new System.Timers.Timer();
animationTimer.Interval = 20;
animationTimer.AutoReset = false; // Only one Ping! We'll activate it if necessary
animationTimer.Elapsed += new ElapsedEventHandler(AnimationStep);
animationTimer.Start();
// Disable the button also, because we don't want another timer instance to
// interfere with our running animation
button1.Enabled = false;
}
Then, create the event that's called when the timer fires:
private void AnimationStep(object source, ElapsedEventArgs e)
{
// The following code needs to be executed in the context of the UI thread.
// We need to use this.Invoke in Forms or this.Dispatcher.Invoke in WPF
this.Invoke((Action)delegate()
{
// Move picture. Note that we don't need to update the display here
// because the UI thread gets time to do its work while the timer waits
// to fire below
if (pictureBox1.Top > 20)
pictureBox1.Top--;
// Show other picture maybe. I use <= here because initially, the
// position of the picture box may already smaller than 20.
if (pictureBox1.Top <= 20)
{
pictureBox1.Visible = false;
pictureBox2.Visible = true;
}
// Or let the timer fire again if we still need to animate
else
{
(source as System.Timers.Timer).Start();
}
}
}
This works as follows: A timer is created that fires once after 20ms. It then moves the picture up one pixel and then either shows the other picture if the animation is finished or starts the timer again for moving the picture up another pixel.
This keeps the UI responsive and still allows you to animate your picture. The downside is, the animation may not be as smooth as you might want it to be in case your windows is moved or other "work" needs be done by the UI thread.
What if initially b is greater than 20? The loop won't run because the condition in the loop is that i < 20. Because the loop is not ran, nothing will ever happen.
Consider changing the condition in your loop. You probably want the picture to move up, as you're reducing the Top-property. Let's say that initially the Top of pictureBox1 is 40. Following code will work:
while(pictureBox1.Top >= 20)
{
pictureBox1.Top--;
System.Threading.Thread.Sleep(20);
Invalidate();
Refresh();
}
Since the Top is now less than 20, the if-statement can be omitted and you can just call:
pictureBox1.Visible = false;
pictureBox2.Visible = true;
Complete code:
while(pictureBox1.Top >= 20)
{
pictureBox1.Top--;
System.Threading.Thread.Sleep(20);
Invalidate();
Refresh();
}
pictureBox1.Visible = false;
pictureBox2.Visible = true;
After the pictureBox1.Top assignment, call:
this.Invalidate();
this.Refresh();
Assuming you're working with WinForms.
my application runs files and every file has it's own running time.
this function get in millisecond the time that the progress time should run:
timerProgress = my timer
pbStatus = my progress bar
public void AnimateProgBar(int milliSeconds)
{
if (!timerProgress.Enabled && milliSeconds != 0)
{
pbStatus.Value = 0;;
timerProgress.Interval = milliSeconds / 100;
timerProgress.Enabled = true;
}
}
and this is my timer that fill the progress bar:
private void timerProgress_Tick(object sender, EventArgs e)
{
if (pbStatus.Value < 100)
{
pbStatus.Value += 1;
pbStatus.Refresh();
}
else
{
timerProgress.Enabled = false;
}
}
my problem is that progress bar runs too fast for example if AnimateProgBar get the value of 12000 (12 seconds) the progress bar runs only for 6-7 seconds.
It's fishy that your code doesn't work. I tried it a few times, and it missed with about 0.6 seconds each time; it seems like the timer is just imprecise.
What you could do is to take care of the time yourself instead of trusting a timer:
WithEvents Tmr As New Timer With {.Interval = 100}
Dim startTime As Date, AnimationTime%
Sub AnimateProgress(ms%)
If ms <= 0 Then Exit Sub
ProgressBar1.Value = 0
AnimationTime = ms
startTime = Now
Tmr.Start()
End Sub
Private Sub Tmr_Tick() Handles Tmr.Tick
ProgressBar1.Value = Math.Min((Now - startTime).TotalMilliseconds / AnimationTime, 1) * 100
If ProgressBar1.Value = 100 Then Tmr.Stop()
End Sub
EDIT - Response to the reply bellow:
Oh sorry, no it's vb.net. I know both the languages just as well, but I prefer vb, and tend to think that everypony else does so too.
Here's the c# version:
DateTime startTime; int animationTime;
void AnimateProgress(int ms) {
if (ms <= 0) return;
progressBar1.Value = 0;
animationTime = ms;
startTime = DateTime.Now;
Tmr.Start();
}
private void Tmr_Tick(object sender, EventArgs e) {
progressBar1.Value = (int)(Math.Min((DateTime.Now - startTime).TotalMilliseconds / animationTime, 1) * 100);
if (progressBar1.Value == 100) Tmr.Stop();
}
You can try with this sample, based on PerformStep method
var progressBar = new System.Windows.Forms.ProgressBar();
progressBar.Maximum = 100;
progressBar.Minimum = 0;
progressBar.Step = 10;
//begin loop
//Your treatment of step
progressBar.PerformStep();
//end loop
msdn link : http://msdn.microsoft.com/fr-fr/library/system.windows.forms.progressbar.performstep(v=vs.80).aspx
You have sample here
private void CopyWithProgress(string[] filenames)
{
// Display the ProgressBar control.
pBar1.Visible = true;
// Set Minimum to 1 to represent the first file being copied.
pBar1.Minimum = 1;
// Set Maximum to the total number of files to copy.
pBar1.Maximum = filenames.Length;
// Set the initial value of the ProgressBar.
pBar1.Value = 1;
// Set the Step property to a value of 1 to represent each file being copied.
pBar1.Step = 1;
// Loop through all files to copy.
for (int x = 1; x <= filenames.Length; x++)
{
// Copy the file and increment the ProgressBar if successful.
if(CopyFile(filenames[x-1]) == true)
{
// Perform the increment on the ProgressBar.
pBar1.PerformStep();
}
}
}
link : http://msdn.microsoft.com/fr-fr/library/system.windows.forms.progressbar.performstep(v=vs.80).aspx
I couldn't reproduce your issue. I just tested a new form, with a ProgressBar and a Timer as you detailed in your question, and merely added one button to start the test, and one label to show the elapsed time:
DateTime start;
private void button1_Click(object sender, EventArgs e)
{
start = DateTime.Now;
AnimateProgBar(12000);
}
private void timer1_Tick(object sender, EventArgs e)
{
label1.Text = DateTime.Now.Subtract(start).TotalSeconds.ToString();
//the rest of your code, starting with "if (pbStatus.Value < 100)"
I consistently got 12.6 seconds until the progressBar filled (and the timer stopped, freezing the label text)... Maybe part of your progressBar is hidden?
[Edit]
If you're curious about the 0.6 extra seconds that BlackCap also noticed, it's because you're setting the timer interval to 120 milliseconds, but timer events have a resolution of about 18 milliseconds, so it will actually fire at 126 instead of 120.
I have a problem. I want to save the remaining seconds in countdown timer (for example, the remaining time = 12 seconds) i want to save that 12 seconds in a variable.
this is my code
int order = 0;
bool right = true;
DispatcherTimer timer1 = new DispatcherTimer();
private void timer_start()
{
timer1.Interval = new TimeSpan(0, 0, 0, 1);
timer1.Tick += new EventHandler(timer1_Tick);
timer1.Start();
}
int remainingSecond;
int tik = 15;
void timer1_Tick(object sender, EventArgs e)
{
this.Timer.Text = tik.ToString();
if (tik > 0)
{
tik--;
if (this.order >= 5)
{
timer1.Stop();
if (right)
{
remainingSecond = tik;
}
else
remainingSecond = 0;
}
}
else
{
remainingSecond = 0;
timer1.Stop();
}
}
everytime I write "remainingSecond" , its value is always 0. I wish that remainingSecond value is 12. Help me, please. Thanks
You assigned order = 0 but did not increase it any where and set this condition
if (this.order >= 5) which will never true. So it will keep decrementing your tik and at the end your this condition if (tik > 0) will become false. So else will be executed and it will set your remainingSecond to ZERO. Thats why you are getting ZERO as output.
Your timer is ticking in every 1milisecond. The timer will start, and it will tick immediately, at that time order will be zero, and your else statement will get executed that will set remainingSeconds to ZERo and will Stop the timer as well. So the clicking on the buttons won't do anything for you.
Try to set the interval for 1 second instead of 1 millisecond
Showing a Spin wheel progress animated gif while user initiates a long running process.
When i click the start, the process starts and same time wheel starts rotating.
But the problem is, the wheel strucks in-between and resumes, that happen multiple times during the long run process. It should be continuously rotation. I am running both the task and animated gif in same thread (since the indicator is just an animated image not a real progress value).
Code used is,
this.progressPictureBox.Visible = true;
this.Refresh(); // this - an user controll
this.progressPictureBox.Refresh();
Application.DoEvents();
OnStartCalibration(); // Starts long running process
this.progressPictureBox.Visible = false;
OnStartCalibration()
{
int count = 6;
int sleepInterval = 5000;
bool success = false;
for (int i = 0; i < count; i++)
{
Application.DoEvents();
m_keywordList.Clear();
m_keywordList.Add("HeatCoolModeStatus");
m_role.ReadValueForKeys(m_keywordList, null, null);
l_currentValue = (int)m_role.GetValue("HeatCoolModeStatus");
if (l_currentValue == 16)
{
success = true;
break;
}
System.Threading.Thread.Sleep(sleepInterval);
}
}
How do I show uninterrupted continuous display of wheel till the process ends?
If you use framework 4, replace the OnStartCalibration(); // Starts long running process line with the following code:
BackgroundWorker bgwLoading = new BackgroundWorker();
bgwLoading.DoWork += (sndr, evnt) =>
{
int count = 6;
int sleepInterval = 5000;
bool success = false;
for (int i = 0; i < count; i++)
{
Application.DoEvents();
m_keywordList.Clear();
m_keywordList.Add("HeatCoolModeStatus");
m_role.ReadValueForKeys(m_keywordList, null, null);
l_currentValue = (int)m_role.GetValue("HeatCoolModeStatus");
if (l_currentValue == 16)
{
success = true;
break;
}
System.Threading.Thread.Sleep(sleepInterval);
}
};
bgwLoading.RunWorkerAsync();
You can't run the progress indication and the task on the same thread. You should use a BackgroundWorker
Your GUI thread will subscribe to the ProgressChanged event, and will be notified of updates to the task. From here, you can update the progress indication appropriately. There's also events for when the task is finished.