I am using the below code to update my progress bar.
ProgressBar.Visible = true;
ProgressBar.Minimum = 1;
ProgressBar.Maximum = PortCount;
ProgressBar.Value = 1;
ProgressBar.Step = 1;
int intdata = 5;
for (int x = 1; x <= intdata; x++)
{
ProgressBar.PerformStep();
}
MessageBox.Show("Done");
But, it is not getting updated during runtime. Is it because the progress bar is in the same thread. If so, how to update this progress from another thread. Help...
You are not giving the message pump time to update the control.
Although either of these are bad, you can do:
Call Refresh on the control
Call Application.DoEvents
Is this Windows Forms? Calling Refresh() on the ProgressBar should do it. You could also use Application.DoEvents though which will let your UI respond to user input instead of locking up during the process.
A better idea would be to use the BackgroundWorker class. Do the work on the background thread and send progress updates to the UI thread via the ReportProgress method. This keeps your UI thread responsive.
Related
I would like to make a dedicated class to update the progress bar in my apps (in this case a WPF progressbar). I did something like this :
public class ProgressBarUpdate : IDisposable
{
private readonly double _delta;
private int _current;
private int _total;
private readonly ProgressBar _pb;
public ProgressBarUpdate(ProgressBar pb, int total)
{
_pb = pb;
_total = total;
// the pb.Maximum is a double so it doesn`t get truncated
_delta = _pb.Maximum / total;
_current = 0;
_pb.Visibility = Visibility.Visible;
}
public void Dispose()
{
_pb.Visibility = Visibility.Collapsed;
_current = 0;
}
public void UpdateProgress()
{
_pb.Value =(int)_delta * (++_current);
}
That i use like this (in the UI thread) :
using (var pu = new ProgressBarUpdate(pb, totalCount)
{
for (x=0; x<totalCount; x++)
{
// operations here
pu.UpdateProgress()
}
}
But the UI, probably blocked, is not updating correctly. What is the best way to display all the progress?
Winforms/WPF program is an Eventing system. There is a single thread which continuously processes events from an event queue. That is its main job and ideally that is the only thing which it should do. Any sort of UI activity generates events in the event queue - like you move your mouse over the window or click something or some other window overlaps your window and then again when it goes away from the overlapped position. All these events are processed by the UI thread and that keeps the UI updated all the time.
Further, Winforms/WPF make it necessary to access and/or update controls and their properties in a thread safe manner by allowing it only on the UI thread.
If you block this UI thread or do some other CPU bound calculation on it, then your UI responsiveness and updated behavior will suffer. Worst case UI will freeze.
Hence the correct answer for you is to do your calculation loop on another worker thread and only update the progress bar UI by marshaling the call to UI thread using the Dispatcher.
However, to answer your question and satisfy your inquisition, here is something that is possible - but it is bad practice and your should never do the following...:
To make it simple, when you update the Value property of the progress bar, it invalidates the progress bar UI - so, UI must update. Hence lets say an event is generated in the event queue which will cause some code to run which will update the UI. However, you are running in a loop over the UI thread - so, the thread has no chance to process this event unless your loop is over. Hence you don't see any UI update. The trick is to make the UI thread process that event before you make the next update on the Value of progress bar. You can do this by forcefully invoking a lower priority item into the event queue - so that normal and higher priority items are processed before going to the next iteration.
using (var pu = new ProgressBarUpdate(pb, totalCount))
{
for (int x = 0; x < totalCount ; x++)
{
// operations here
pu.UpdateProgress();
Dispatcher.Invoke(DispatcherPriority.Background, new Action(()=>{}));
}
}
If you're doing your work, and calling UpdateProgress, on the UI thread then it won't update until you finish the work and the UI thread can do other work (like refresh the UI). So this will never work.
If you're doing your work on a background thread, then you need to use a Dispatcher to marshal the setting the value to the UI thread.
Here's an example from http://tech.pro/tutorial/800/working-with-the-wpf-dispatcher
if (!myCheckBox.Dispatcher.CheckAccess())
{
myCheckBox.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
new Action(
delegate()
{
myCheckBox.IsChecked = true;
}
));
}
else
{
myCheckBox.IsChecked = true;
}
Try this:
public ProgressBarUpdate(ProgressBar pb, int total)
{
_pb = pb;
_total = total;
_delta = _pb.MaxValue/((double)total); /make sure you do not truncate delta
_current = 0;
_pb.Visibility = Visibility.Visible;
}
public void Dispose()
{
_pb.Visibility = Visibility.Collapsed;
_current = 0;
}
public void UpdateProgress()
{
_pb.Value = (int)( _delta * (++_current)); //update after the increment
}
I suggest also using float instead of double.
You've been saying you want to avoid using threads, I assume because you don't want unnecessary complication, but it's really not a big deal. It's a very simple matter to make an operation multi-threaded. Even for very short and simple tasks, this is the most straightforward way to achieve what you want. Using TPL, it would look something like this:
using System.Threading.Tasks;
...
Task.Factory.StartNew(() => {
for (...) {
// operation...
progressBar.Dispatcher.BeginInvoke(() => progressBar.Value = ...);
}
});
i am programming a benchmark tool, that reads a bunch of variables from a local server in a thread.
int countReads = 1000;
Int64 count = 0;
for (int i = 0; i < countReads; i++)
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
DateTime start = DateTime.Now;
session.Read(null, 0, TimestampsToReturn.Neither, idCollection, out ReadResults, out diagnosticInfos);
DateTime stop = DateTime.Now;
Thread.CurrentThread.Priority = ThreadPriority.Normal;
TimeSpan delay = (stop - start);
double s = delay.TotalMilliseconds;
count += (Int64)s;
Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
{
progressBar1.Value = i;
}));
}
double avg = (double)count / countReads;
Dispatcher.Invoke(DispatcherPriority.Input, new Action(() =>
{
listBox1.Items.Add(avg);
}));
I am calculating the timespan it took to proceed the read and getting the average timespan at the end.
DateTime start = DateTime.Now;
session.Read(null, 0, TimestampsToReturn.Neither, idCollection, out ReadResults, out diagnosticInfos);
DateTime stop = DateTime.Now
if i run the code without updating the progressbar it took about 5ms average.
but if i run it with
Dispatcher.Invoke(DispatcherPriority.Render, new Action(() =>
{
progressBar1.Value = i;
}));
it takes about 10 ms average.
My question is, why is the timespan higher when using the progressbar?
i am just calculating the timespan for the read. Not including the progressbar update.
Is there any way to evacuate the ui-painting so that it doesn´t effect my read-timespan?
Thanks for your help.
Best regards
Stop using Invoke to transfer progress information to the UI thread. Publish the progress information to a shared data structure or variable and have the UI thread poll for it using a timer on a reasonable interval. I know it seems like we have all been brainwashed into thinking Invoke is the be-all method for doing worker-to-UI thread interactions, but for simple progress information it can be (and often is) the worst method.
A polling method using a timer on the UI thread offers the following benefits.
It breaks the tight coupling that Invoke imposes on both the UI and worker threads.
The UI thread gets to dictate when and how often it should update the progress information instead of the other way around. When you stop and think about it this is how it should be anyway.
You get more throughput on both the UI and worker threads.
I know this does not directly answer your question as to why session.Read appears to run slower. Try changing your strategy for updating progress information from a push model (via Invoke) to a pull model (via a timer). See if that makes a difference. Even if it does not I would still stick with the pull model for the reasons listed above.
Here is what MSDN says about Dispatcher.Invoke
Executes the specified delegate synchronously on the thread the Dispatcher is associated with.
So, basically, Dispatcher.Invoke blocks until the dispatcher thread as handled the request.
Try Dispatcher.BeginInvoke instead.
If current executing thread is associated with Dispatcher you are using - Invoke() will block this thread so in this case try out using Dispatcher.BeginInvoke() it will do the job asynchronously.
MSDN, Dispatcher.Invoke Method:
Invoke is a synchronous operation; therefore, control will not return
to the calling object until after the callback returns.
BTW, just of interest try out DispatcherPriority.Send
I came 9 years late to the party, but I think this is an even easier solution: Just wait until the progress bar value reaches a certain threshold before updating it. In my example, I refresh the toolbar every fifth of the maximum value.
private static int progressBarMaxValue = -1;
private static int progressBarChunkSize = -1;
public static void progressBarSetNotRealTimeValue(ProgressBar progressBar, int argNewValue)
{
if (progressBarMaxValue != -1)
{
if (argNewValue < progressBarChunkSize)
{
//Threshold not reached yet, discard the new value.
return;
}
else
{
//Allow the update, and set the next threshold higher.
progressBarChunkSize += progressBarChunkSize;
}
}
if (Thread.CurrentThread.IsBackground)
{
progressBar.BeginInvoke(new Action(() =>
{
if (progressBarMaxValue == -1)
{
progressBarMaxValue = progressBar.Maximum;
progressBarChunkSize = progressBar.Maximum / 5;
}
progressBar.Value = argNewValue;
}));
}
else
{
progressBar.Value = argNewValue;
}
}
This code is from my C# WPF application:
public MainWindow()
{
InitializeComponent();
status("Getting dl links");
getLinks();
}
The procedure getLinks currently displays a couple of links in a messagebox. Those links are displayed in a messagebox before the WPF application becomes visible.
In this is case not a problem, but how would I show progress (like a progressbar) of any
procedure I want to load at startup?
Here is an example on how you can do it. To simplify it a bit, I added the controls directly in the MainWindow constructor, but I would prefer to do this with XAML.
public MainWindow()
{
InitializeComponent();
var progressBar = new ProgressBar();
progressBar.Height = 40;
progressBar.Width = 200;
progressBar.Margin = new Thickness(100, 100, 100, 100);
Task.Factory.StartNew(() =>
{
// getLinks();
for (var i = 0; i < 5; i++)
{
Dispatcher.Invoke(new Action(() => { progressBar.Value += 20; }));
Thread.Sleep(500);
}
});
var stackPanel = new StackPanel();
stackPanel.Children.Add(progressBar);
Content = stackPanel;
}
I first add a ProgressBar somewhere on the interface to make it visible for this demo and then I add it to a new StackPanel, it could be any panel at all, in this case it doesn't matter.
To load the links on another thread, I create a new Task, this is a part of the TPL (Task Parallel Library) in .NET 4.0. In this case I am simulating that getLinks() takes 5 * 500 milliseconds to run and that it in fact is five links that will be loaded, hence 20% each iteration.
What I do then is that I add 20 to the progressBar value, which indicates that it increased with 20%.
This line might confuse you a bit
Dispatcher.Invoke(new Action(() => { progressBar.Value += 20; }));
But it is in fact quite common when you do cross-thread programming with GUI. So the problem is that you are on another thread here, we started of a Task that will run on a separate thread, and you cannot update your UI thread from another thread. So what you need is something called a Dispatcher, and this is accessable from within your Window-class.
Then you Invoke an action on it, which means that you simply say "Run this piece of code on this thread for me".
And if you want to display a MessageBox when everything is loaded, you can simply add a MessageBox.Show("Loaded!"); after the for-loop.
Any 'loading' tasks need to happen on a background thread (see the BackgroundWorker class - google for lots of examples). That way, the UI thread is free to show your window and update your window with status messages. Otherwise, the UI thread is blocked from doing anything until your loading is complete.
I have this code in a winform app:
for (int i = 1; i <= 20; i++)
{
lbl.Text = i.ToString();
Thread.Sleep(100);
}
I expected to see the label progress from 1 to 20 but instead it just hangs while the for loop is running and then displays a 20, i.e. I don't see 1-19.
Why is this and is there a way to update the label text quickly similar to the milliseconds on an analogue clock (I'm not making a clock, just an example.)
Thanks
*EDIT: This also happens if I have a button with an event that increments the number without Thread.Sleep but I have a beginInvoke to play a wav file *
You are sleeping the gui thread, so before the gui gets to update its slept again.
Try using a timer with a tick of 100ms
Call Application.DoEvents(); before sleep, that will process all messages in the queue:
for (int i = 1; i <= 20; i++)
{
lbl.Text = i.ToString();
Application.DoEvents();
Thread.Sleep(100);
}
The application hangs and you only see 20 simply because this would appear to be executing on the UI thread and you're Sleeping it, and therefore choking the window message loop. However, even if that wasn't the case, you'd most likely only ever see 20 anyway as the loop would execute so fast.
Use a System.Threading.Timer to execute every x milliseconds and update your label by invoking the appropriate method on the UI thread.
The following post demonstrates how to implement what you might want (albeit in the WPF environment, but the principle is the same - use ISynchronizeInvoke for WinForms as opposed to the Dispatcher shown in WPF here):
Timer callback closes WPF app (DispatcherTimer works..)
Use Timer control
int i=0;
private void timer_Tick(object sender, EventArgs e)
{
if(i <=20)
{
lbl.Text = i.ToString();
}
i++;
}
Set TimerInterval according to your need (Interval of 1000 = 1 second)
Suppose I have some code which is running in the UI thread, which spawns a new thread to do work in the background, and then goes on to do UI work. When the background work is done, BeginInvoke is called from the new thread. If the UI work is still going on, will the callback given to BeginInvoke interrupt the UI work, or will it wait?
Code example - add a button called button1 to a form and add button1_Click to its click event, and then add this to the form:
bool continueWork = true;
private void button1_Click(object sender, EventArgs e)
{
Thread workThread = new Thread(performBackgroundWork);
workThread.IsBackground = true;
workThread.SetApartmentState(ApartmentState.STA);
workThread.Start();
for (long i = 0; i < long.MaxValue; i++)
{
if (!continueWork)
break;
button1.Text = i.ToString();
}
}
private void performBackgroundWork()
{
Thread.Sleep(1);
button1.BeginInvoke(new MethodInvoker(stopUIWork));
}
private void stopUIWork()
{
continueWork = false;
}
What is button1's text after it is clicked?
BeginInvoke adds the delegate to a queue (the message queue to be exact). So the answer is no, they won't get interrupted. Your button click handler is in fact executed due to a message in the message queue as well.
EDIT: Sorry, I forgot to answer your second question, but you can probably figure that out by yourself. The button text will be long.MaxValue - 1. You would probably want to execute stopUIWork without using BeginInvoke.