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 = ...);
}
});
Related
I have a button, clicking it will processing all files. I want to display the progress when running it. So if file 1 is processing, then the UI displays
"processing file 1"
; when processing file 2, the UI displays
"processing file 1"
"processing file 2"
So I use a Listbox to do it. The ItemSource of the ListBox is a collection in my ViewModel
private ObservableCollection<string> _displayedFiles;
public ObservableCollection<string> DisplayedFiles
{
get {return _displayedFiles;}
set
{
_displayedFiles = value;
PropertyChanged(nameof(DisplayedFiles));
}
}
Now the ViewModel is passed to the command class
public class MyCommand :ICommand
{
private MyViewModel myViewModel;
public MyCommand(MyViewModel myViewModel)
{
this.myViewModel = myViewModel;
}
public void Execute(object parameter)
{
foreach(var f in files)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, (ThreadStart)delegate () { AddFiles(f);});
}
}
private void AddFiles(string f)
{
this.ViewModel.DisplayedFiles.Add(f);
}
}
However I found the UI is freeze and the list is not displayed one by one. It displays the whole bunch list together after the loop iteration completed.
When you use Dispatcher.Invoke - you are basically telling the GUI thread to do something.
Since you are doing heavy lifting in that thread the GUI will freeze.
For the progress bar things to work, you always do the time taking intensive task on a different thread and only do progress bar GUI updates on GUI thread.
Let me show you a basic psuedo example:
Imagine my code is:
private void DoTheTaskAndUpdateGUI(List<string> filesToProcess)
{
int completionPct = 0;
for(int idx = 0;idx<filesToProcess.Count; idx++)
{
ProcessFile(filesToProcess[idx]);
completionPct = ((idx + 1)/filesToProcess.Count) * 100;
UpdateGUI(completionPct);
}
}
If all of this is running on single GUI thread, my UI will always be frozen until the loop finishes. To make UI a bit responsive, I can rearrange the fucntionality like:
private void DoTheTaskAndUpdateGUI(List<string> filesToProcess)
{
var guiDispatcher = Dispatcher.CurrentDispatcher;
Task.Factory.StartNew(()=>
{
int completionPct = 0;
for(int idx = 0;idx<filesToProcess.Count; idx++)
{
ProcessFile(filesToProcess[idx]);
completionPct = ((idx + 1)/filesToProcess.Count) * 100;
guiDispatcher.Invoke(UpdateGUI(completionPct));
}
});
}
I have written this code in notepad so don't assume it compiles successfully. but understand the thought behind it.
DoTheTaskAndUpdateGUI method is called on GUI Thread
We hold the Dispatcher instance
A new Thread/Task starts and the heavy lifting
begins in it
GUI Thread is now free
Whenever the intensive task
reaches Dispatcher.Invoke call it updates GUI by requesting GUI
thread to do so
So basically, I'm creating music software and would like the ability to pause/stop the song whenever necessary. However, when the song is playing it doesn't allow any controls on the form to be used and the "KeyDown" event also seems unaffective, therefore I can't stop the song until it's over.
I've tried using an instance of the "thread" class to do this but the problem is that I require the use of certain "Label" controls on the form, which seems to be hard to do from another thread.
Here's the gist of my "playing" code.
Sound[] notes = song.Sounds;
double seconds = 0;
foreach (Sound sound in notes)
{
seconds += (double)sound.NoteType.GetDuration(tempo) / 1000;
}
timeleft = seconds;
lblSecondsLeft.Text = timeleft + "";
double length = notes.Length;
double thing = (100 / length);
double otherthing = Math.Round(thing);
sounds = notes;
double progress = 0;
for (int i = 0; i < notes.Length; i++)
{
Sound sound = notes[i];
lblTempo.Text = tempo.ToString();
string note = sound.Note.ToString();
lblNote.Text = note;
progress += thing;
lblProgress.Text = progress + "%";
seconds -= (double)sound.NoteType.GetDuration(tempo) / 1000;
lblSecondsLeft.Text = seconds + " seconds left";
sound.PlaySound(song.Channel);
}
It's an annoyance, but it's a well known annoyance. You have to run the non-UI code on a separate (non-UI) thread so that the UI stays responsive to buttons and text updates and whatnot. However, this means that you have to update the UI from the non-UI thread by switching to the UI thread during the update. The following code does this.
Note 1: This is trimmed from some code I have to hand, so it's not properly checked.
Note 2: This is a WPF version, WinForms is slightly different but the principle is identical.
public String LabelText{
get {
if (!this.Dispatcher.CheckAccess()) {
return (String)this.Dispatcher.Invoke(new Func<String>(() => { return LabelText; }));
}
return Label.Text;
}
set {
if (!this.Dispatcher.CheckAccess()) {
this.Dispatcher.Invoke(new Action(() => { LabelText = value; }));
return;
}
LabelText = value;
}
}
This works by checking to see if get/set is on the right thread, if it's not then it switches thread and calls itself (now on the correct thread), completes the get or set and returns. You can also use BeginInvoke if you don't need the thread to wait for the screen update, which for real time music may well be the case.
I have a WPF application and I am working with the .NET Framework 4.0 and C#. My application consists of an interface with several controls. In particular I have a task that needs to be executed periodically every 10 seconds. In order to execute it I use a System.Windows.Threading.DispatcherTimer. The ViewModel looks like this:
public class WindowViewModel {
protected DispatcherTimer cycle;
public WindowViewModel() {
this.cycle = new DispatcherTimer(DispatcherPriority.Normal,
System.Windows.Application.Current.Dispatcher);
this.cycle.Interval = new TimeSpan(0,0,0,0,10000);
this.cycle.Tick += delegate(object sender, EventArgs e) {
for (int i = 0; i < 20; i++) {
// Doing something
}
};
this.cycle.Start;
}
}
As I said the routine called periodically does something. In particular there is some heavy logic there which causes that routine to take some seconds to execute and complete. Well it is a different thread so I should be ok and the interface is not supposed to freeze.
The problem is that that routine causes the viewmodel to be updated. Several data are updated, and the corresponding View is bound to those data. What happens is that all updated data are refreshed once at a time when the routine completes. I want data to be updated during the thread execution.
In particular inside that routine I have a for cycle. Well at the exit of the cycle everything is updated in the interface. How to achieve this? Where am i doing wrong?
The DispatcherTimer uses the supplied Dispatcher to run the timer callback.
If you take a look at the docs for Dispatcher, there's a clue:
Provides services for managing the queue of work items for a thread.
So, by using the System.Windows.Application.Current.Dispatcher, you're using the Dispatcher that manages "the queue of work items" for the UI thread.
To run your work in the ThreadPool instead, you could either use System.Threading.Timer or use ThreadPool.QueueUserWorkItem in your DispatcherTimer callback.
If you combine this with the following extension method, it becomes easy to marshal any UI specific stuff back to the Dispatcher when you finish your heavy workload:
public static class DispatcherEx
{
public static void InvokeOrExecute(this Dispatcher dispatcher, Action action)
{
if (dispatcher.CheckAccess())
{
action();
}
else
{
dispatcher.BeginInvoke(DispatcherPriority.Normal,
action);
}
}
}
then...
this.cycle.Tick += delegate(object sender, EventArgs e) {
ThreadPool.QueueUserWorkItem(_ => {
for (int i = 0; i < 20; i++) {
// Doing something heavy
System.Windows.Application.Current.Dispatcher.InvokeOrExecute(() => {
//update the UI on the UI thread.
});
}
});
};
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;
}
}
Im trying to update a progress bar while doing some data type checks on a separate thread and there seems to be a delay between what value the progress bar is at and the value which is actually show.
The following code is executed by the non-GUI thread and is used to raise the event.
protected virtual void OnUpdateProgressBar(object sender, ProgressBarEventArgs e)
{
EventHandler<ProgressBarEventArgs> TempHandler = UpdateProgressBar;
//Avoid possible race condition.
if (TempHandler != null)
{
TempHandler(this, e);
}
}
I have created a separate class for updating the progress bar and when i create an instance of it, i pass a reference to the progress bar. Below is the entire class.
public class ProgressBarChanged
{
ProgressBar statusBar;
public ProgressBarChanged(ProgressBar pb)
{
statusBar = pb;
statusBar.Value = 0;
}
public ProgressBarChanged()
{
}
public void subscribeToEvent(DataVerification test)
{
test.UpdateProgressBar += new EventHandler<ProgressBarEventArgs>(incrementPB);
}
public void incrementPB(object sender, ProgressBarEventArgs e)
{
Action action = () =>
{
if (e.CurrentRow == e.FinalRow - 10)
{
int i = 5;
}
statusBar.Maximum = e.FinalRow;
statusBar.Value = e.CurrentRow;
};
if(statusBar.InvokeRequired)
statusBar.Invoke(action);
else
action();
}
}
I have uploaded a screen shot showing the progress bar and the actual values.
Any ideas???
Thanks
The progessbar is a simple feedback to the user, not a piece of exact instrumentation. It's a pacifier.
It also incorporates it's own async logic to update the screen (independent of the message loop). This makes that it may run a little behind.
What's the big deal?
To get more accurate results, divide your range into < 100 segments and do fewer updates.
A delay is pretty normal. After all, invoking a method in the UI thread means Windows will dispatch a message and if your thread is fast enough (and CPU consuming) then it'll appear faster than UI.