C# wpf, how to update a progressbar over 1 second - c#

I cannot for the life of me figure out how to have a progress bar "progress" (fill up) over 1 second.
I do NOT mean one update every one second, what I want is:
After 100 milliseconds, 10% progress
after 200ms, 20% progress
...
after 1000ms, 100% progress.
How does one do this?

await Application.Current.Dispatcher.InvokeAsync(() =>
{
for (int i = 10; i <= 100; i += 10)
{
yourprogressbar.Value = i;
Thread.Sleep(100);
}
});
You need to add async keyword on your method to be able to use await.

Related

UWP ProgressBar not fully updating from a For loop

I am trying to update a ProgressBar from a For loop, in my head, should be easy but I'm struggling a bit.
I've got a Dispatcher method which only seems to fire off twice. Once after the first loop, then not until it has completed. I.e. the loop count should be 6, so each increment would be 16.666667. The ProgressBar will update once to 16.666667 then will wait until the loop has complete to reach 100.
I've read through a few different questions on here, but can't find the answer. I'm still relatively new to this, so any help explained in the dumbest way possible would be appreciated :)
double progressIncrease = (double)100 / TodaysFixtures.Count;
ProcessingBar.Value = 0;
for (int fixture = 0; fixture < TodaysFixtures.Count; fixture++)
{
// ...
await UpdateProcessingBarAsync(progressIncrease);
}
private async Task UpdateProcessingBarAsync(double progressIncrease)
{
await Window.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ProcessingBar.Value += progressIncrease;
});
}

In what time interval is the conditions in a while clause checked?

How do I wait for a specified time while showing the remaining time to wait?
I now solved it like this but I feel like this is a really bad way to do it:
//This is running in a BackgroundWorker:
Stopwatch watch = new Stopwatch();
watch.Start();
while(watch.ElapsedMilliseconds != SecondsToWait * 1000)
{
TimeToNextRefresh = ((SecondsToWait * 1000) - watch.ElapsedMilliseconds) / 1000;
Thread.Sleep(1);
}
watch.Stop();
So here I am guessing that the condition (watch.ElapsedMilliseconds != SecondsToWait * 1000) is checked every millisecond.
So the main question is; In what period is the condition of while checked and/or how do I improve the code I've written?
It depends on what's the code inside while loop!
For example, if you write some really long/time-consuming code in a while loop, each iteration of the while loop, or course, will be longer than a while loop that only has short/fast code.
Compare these two while loops:
while (true) {
Console.WriteLine("Hello");
}
and
while (true) {
Console.Beep(5000);
}
Each iteration of the first while loop is faster than that of the second one because Console.Beep(5000) takes 5 seconds and Console.WriteLine only takes a fraction of a second.
So you can't rely on while loops to count time.
This is what you should do:
Create an instance of System.Windows.Forms.Timer, not the System.Timers.Timer nor the System.Threading.Timer. I find the first one the most useful (others are more advanced).
Timer timer = new Timer();
timer.Interval = 1000; // 1000 means 1000ms aka 1 second
timer.Tick += TimerTicked;
timer.Start();
Now the compiler will tell you that TimerTicked is not defined, so let's go define that:
private void TimerTicked(object sender, EventArgs e) {
}
Now you're all set. The code in TimerTicked will be called every one second.
Let's say you want to measure a time of 10 seconds. After 10 seconds, you want to do something. So first create a variable called secondsLeft in the class level:
int secondsLeft = 10;
Now in TimerTicked, you want to check whether secondsLeft is 0. If it is, do that something, else, minus one:
if (secondsLeft == 0) {
DoSomething();
} else {
secondsLeft--;
}
And secondsLeft is the time remaining! You can display it on a label or something.
To pause the timer, simply
timer.Stop();
The exact interval in which your while condition is checked is hard to predict. Thread.Sleep(1); only tells the operating system that you want your thread to sleep for at least 1 millisecond. There is no guarantee that your thread will be active again after exactly 1ms. Actually you can rather be sure that it will be more than that. The thread is scheduled again after 1ms, but there will be a delay until he gets his CPU time slot.
The interval you want for your loop actually depends how you want to display the remaining time. If you want to display only seconds, why would you update that display every millisecond, although the text would change only every 1000ms?
A loop like that is probably not a good way to implement something like that. I would recommend a System.Threading.Timer:
// this Timer will call TimerTick every 1000ms
Timer timer = new Timer(TimerTick, null, 0, 1000);
and implement the handler
public void TimerTick(object sender)
{
// update your display
}
Note that you will have the "update your display" part on the UI thread again, as this method is called by the Timer on a different thread.
This code is can really make an infinite loop if a calculation just take longer than 1 miliseconds.
You can achieve your desired behaviour with a simple System.Winforms.Forms.Timer like this snipped below :
private int tickCount = 0;
private int remaining = 10;
private void timer1_Tick(object sender, EventArgs e)
{
remaining--;
textBox1.Text = remaining.ToString();
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Interval = 1000;
timer1.Enabled = true;
}
With this you can countdown from 10 seconds and every tick you write to a textbox the remaining seconds

Threading not working?

So! I am just playing around with progress bars in winForm but I noticed something. If I use a For statement the progress bar goes from 0 - 100 instant, even if I put a EX: Thread.Sleep(10000); It waits the time and then goes too 100%.
What am I doing wrong?
public void progressbar(object sender, EventArgs e)
{
for (int i = 0 ; i < 100; i++)
{
Thread.Sleep(10);
progressBar1.Value = i;
}
}
You're blocking the UI thread. While your event handler is running, your window can't handle any incoming window messages, so it will not update, and it will not repaint. Don't block the UI thread.
You need to update every interaction your progressbar. Note also that in your for, will go to 90 only and not to 100. Try this code:
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(10);
progressBar1.Value = i;
progressBar1.Refresh();
}
EDIT:
To work you must put in some event such as the click of a button , never in a Form Load

Refresh progress bar

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!
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();
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.

Display number as it is found

I have simple task in my app, but i have problem.
For example:
for (int i = 1; i <= 1000000; i++)
{
if (i % 2 == 0) {
TextBlock.Text += string.Format("{0},", i);
}
}
While app doing that task it takes long time, so I would like it displays number when it is found and not at the end, all numbers together.
What is the simplest way to do that for newbie like me?
It is WP8, C#.
Thanks a lot.
you can keep adding these numbers in a queue object and have a thread looking for a change in the queue object and simultaneously update the textbox.
So the problem is, that UI doesn't get refreshed until your loop end, even if you are appending text to TextBox. The easy way to fix it - is to add Application.DoEvents() call after TextBlock.Text += string.Format("{0},", i);.
But it has some critical downsides (explained here https://stackoverflow.com/a/5183623/2152334).
The right way is to do calculation in a different thread (using Tasks for example) and update UI thread using Dispatcher:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
TextBlock.Text += ...
});
You can't refresh the display if your code blocks the UI thread. You need to leave the UI thread for your long task.
// We go to a non-UI thread
TaskEx.Run(() => {
for (int i = 1; i <= 1000000; i++)
{
// some long operation here... -_-"
if (i % 2 == 0)
{
// We return to UI thread after long operation to display the result
Deployment.Current.Dispatcher.InvokeAsync(() => {
TextBlock.Text += string.Format("{0},", i);
});
}
}
});
Also you might consider adding a cancellation token to break the loop in case the user leaves the page being displayed.
[edit: this solution is meant for your comment "my app will display few numbers but calculation takes long time". If you really need 1000000 results, try to Dispatcher.InvokeAsync(...) and TextBlock.Text += ... multiple results at a time.]

Categories