Update Progressbar from another class - c#

I'm writing an app in C#/WPF (.Net framework 4.0). The one thing I am having a problem with is how to update controls, if possible, while the app is very busy. I think I'm just flailing about trying things I find on the web to make it work (and on rare occasions it does).
The app has four classes, a main window class, a Worker1 class and a Worker2 class and a static ExtensionMethods class. Worker2 is called from Worker1 and does most of the work. When it runs the CPU is at 100% (core at 100%, system has four cores). (All are in the same namespace.)
public static class ExtensionMethods
{
private static Action EmptyDelegate = delegate() { };
public static void Refresh(this UIElement uiElement)
{
uiElement.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, EmptyDelegate);
}
}
The main window calls a top level function in Worker1, which goes through some setup and then starts the long process, which goes something like this:
MainWindow mainWindow = App.Current.MainWindow as MainWindow;
mainWindow.dispatcher_UpdateProgressBarMaximum(maxValue);
int count=0;
Worker2 worker2 = new Worker2();
while (bytesRead =(readData() !=0) {
count++;
worker2.doWork();
mainWindow.dispatcher_UpdateProgressBar(count);
}
In the mainWindow class is:
dispatcher_UpdateProgressBar(int Value)
{
Dispatcher.BeginInvoke(new Action(() => myProgressBar.Value = value), null);
myProgressBar.Refresh();
}
(I have also tried it with: myProgressBar.Dispatcher.Begin...etc.
but neither matter too much. With small files where there are 2 or 3 reads I can see the progress bar getting updated. But with large files, no update until the end.
This happens also for some label controls in which I display some status information. After the entire work is completed I optionally display a messagebox of what was done. For the labels, when I display the messagebox, the labels are updated. The app may work on many files one after the other.
I've thought about using a background thread but not sure how to do it. That is, the worker2.doWork() section is very intensive but if that's on it's own thread the the mainWindow.dispatcher_Update...will be called immediately after the thread begins and there's nothing to stop the next read from happening, right?
Perhaps it's impossible the way I've coded it. IF I used a Backgroundworker, would the class be instantiated in the Main Window or Worker1? Even a pointer or two would be great.

In .NET 4.5 they added the IProgress<T> interface and the Progress<T> class that lets you report progress from another thread and it will automatically run the callback on the UI thread if the Progress object was created there.
Being that you are on .NET 4.0 you are not totally out of luck, Microsoft released those classes as a Out of band update to the framework via the NuGet Package Microsoft.BCL which back-ports those two classes in to .NET 4.0.
Once you have Progress<T> it is fairly easy to use.
void StartWorker()
{
var progress = new Progress<int>(UpdateProgressBar);
worker1.Start(progress)
}
void UpdateProgressBar(int Value)
{
//This code is invoked on the UI thread
myProgressBar.Value = value;
}
//Elsewhere in Worker1
void Start(IProgress<int> progress)
{
int count=0;
Worker2 worker2 = new Worker2();
while (bytesRead =(readData() !=0)
{
count++;
worker2.doWork();
progress.Report(count);
}
}
However that all being said you should try to rethink your data model. "Embrace WPF" and start using concepts like MVVM to set up bindings and you update data objects that then update your UI (And you can put all your UI marshaling code in your OnPropertyChanged event in your ViewModel).

Use a BackgroundWorker for your background work and update you GUI via the ReportProgress call and handle it by servicing the ProgressChanged event, which contains "progress" - just update your progress bar from there.
If you need to start another background task when the first one finishes you could do that from the RunWorkerCompleted event.

Related

Not updating GUI in time

I have a class that updates a GUI element
public class UpdateLabelClass
{
static MainGUI theForm = (MainGUI)Application.OpenForms[0];
Label lblCurProgress = theForm.curProgress;
public ProgressBarUpdate()
{
}
public void UpdateLabel(String newLabel)
{
lblCurProgress.Text = newLabel;
}
}
And in other classes, I make an instance of the class and call the UpdateLabel(someString);
Now the problem is, it skips the operation of updating the label, so I thought "Maybe it isn't even reaching the code", so I put a MessageBox.Show() right after it, and it updated the label.
What are possible causes to skip the label update, but perform it when I put a message bow right after? Is the program going to fast?
Most likely you are improperly running a long operation in the main UI thread which prevents the label from updating. You could "fix" this by calling DoEvents():
public void UpdateLabel(String newLabel)
{
lblCurProgress.Text = newLabel;
Application.DoEvents();
}
But this is just a band-aid on top of a bad design. You should properly move that code to a background thread and use a delegate/Invoke() to update the label.
Edit: (answering followup question)
By default, your application runs in a single thread. This includes the code that you add to control events, as well as the code that you can't see that is running behind the scenes to make your application respond in the way you'd expect. Things like user interaction (mouse clicks, keyboard presses, etc.) and painting messages (when controls are changed, your window is obscured) are placed into a queue. Those pending messages in the queue only get processed once your code has stopped running. If you have a lengthy chunk of code running, like a long loop, then those messages just sit in the queue waiting to be processed. Thus the update to the label doesn't occur until after your loop is done. What DoEvents() does is tells the application to process those pending messages in the queue, right now, and then return to the code that was currently executing. This allows the label to update in real-time like you expect it to.
When you encounter situations that are "fixed" by DoEvents(), it simply means that you are attempting to run too much code in the main UI thread. The main UI thread is supposed to be focused on responding to user interaction and keeping the display updated. Code in control event handlers should be short and sweet, so that the main UI thread can get back to doing its main job.
The proper fix is to move that lengthy code to a different thread, thus allowing the main UI thread to respond and keep itself updated. For many scenarios, the easiest approach is to place a BackgroundWorker() control on your form and wire up the DoWork(), ProgressChanged() and RunWorkerCompleted() events. *You have to set the WorkerReportsProgress() property to true, however, to handle the ProgressChanged() event. The latter two events are already marshaled to the main UI thread for you so you don't need to worry about cross-thread exceptions. From the DoWork() handler, you call ReportProgress() and pass out a progress percentage value and an optional other object (it could be anything). Those values can be retrieved in the ProgressChanged() event and used to update the GUI. The RunWorkerCompleted() event fires when all the work in the DoWork() handler has been finished.
In your case, you've got a separate class that is doing the work. You can mirror what the BackgroundWorker does by manually creating your own thread in that class to do the work. When you want to update progress, make your class raise a Custom Event that the main form subscribes to. When that event is received, however, it will be running in the context of the separate thread. It is necessary, then, to "marshal" the call across the thread boundaries so that the code is running in the main UI thread before you update the controls. This is accomplished by using delegates ("pointers" to methods) and the Invoke() method. *There are other methods to accomplish this task as well, such as a SynchronizationContext.
See here for some examples of these approaches.
Finally, here is a super simple example of a class that raises custom events from a separate thread:
public partial class Form1 : Form
{
private Clock Clk;
public Form1()
{
InitializeComponent();
Clk = new Clock();
Clk.CurrentTime += new Clock.TimeHack(Clk_CurrentTime);
}
private void Clk_CurrentTime(string hack)
{
if (label1.InvokeRequired)
{
Clock.TimeHack t = new Clock.TimeHack(Clk_CurrentTime);
label1.Invoke(t, new object[] { hack });
}
else
{
label1.Text = hack;
}
}
}
public class Clock
{
public delegate void TimeHack(string hack);
public event TimeHack CurrentTime;
private Thread t;
private bool stopThread = false;
public Clock()
{
t = new Thread(new ThreadStart(ThreadLoop));
t.IsBackground = true; // allow it to be shutdown automatically when the application exits
t.Start();
}
private void ThreadLoop()
{
while (!stopThread)
{
if (CurrentTime != null)
{
CurrentTime(DateTime.Now.ToString());
}
System.Threading.Thread.Sleep(1000);
}
}
public void Stop()
{
stopThread = true;
}
}
public void UpdateLabel(String newLabel)
{
lblCurProgress.Text = newLabel;
lblCurProgress.Refresh();
}

how to create thread work with gui

I'm novice in program with c#. I want to create thread that move label in the main UI without stuck the UI until the movement done
I built something but it didnt work
tell me what is my problem
private void button1_Click(object sender, EventArgs e)
{
Thread t = new Thread(Movelb);
t.IsBackground = true;
t.Start();enter code here
}
private void DOsomeThing()
{
label2.Visible = true;
label2.Location = new Point(0, 205);
for (int i = 0; i < 533; i++)
{
label2.Location = new Point(i, 205);
Thread.Sleep(10);
}
label1.Text="false";
}
private void Movelb()
{
if (this.InvokeRequired)
{
threadDel d = new threadDel(DOsomeThing);
this.BeginInvoke(d);
}
else
DOsomeThing();
}
Do not use threads to paint to forms or modify/update form contents. The recommended paradigm in Windows programming is One Thread Per Form or Window. If you want to create forms that run from separate threads, then you must
create the new thread first
create the Form on the new thread
In this way, the new thread will serve as the new Form's message handler. But even then, you should still do all manipulation of the Form within that thread (and if the form wants to modify contents in another form running on a different thread, then some additional thread-safe communication trickery may be required).
To animate window contents, you should use System.Windows.Forms.Timer instead, which executes on the Form's thread in lock-step with its other messages. You'll need to re-implement your animation as a state machine rather than a for() loop construct, though. That means the variables for Label position will need to be embedded into the Form class, so that updates can be preserved across Timer message invocations.
You need to understand the event model first. In event-driven environments like Windows or Android or Linux etc... the "automatic " tasks such as animations of coordinates or other properties are usually done using Timers that keep re-sending events back to the handler that advances the animation/process. In your particular example - if you need to move label, use Widows.Forms.Timer. It is not appropriate to block UI thread that processes events with lengthy tasks as UI thread will stall and your app will freeze or become jerky. NOW, on the other hand there are many cases when adding extra threads DOES help a lot, when? Not in your case, because you only change the coordinate of the label that is nothing in terms of CPU in comparison to repaint, so your solution with extra thread is LESS efficient and much more complex than using timer. An extra thread is beneficial only when the logical work it performs on animation model is comparable or out-weights the paint work- imagine a game where 200 bugs need to be animated on screen according to many logical rules, in this case bug painting may be done in UI thread, but bug property changes/animations may be done in another thread if those computations are intense.
How Events work?
An OS has an infinite loop inside that gets interrupted by keyboard, mouse and other events but the loop spins indefinitely until you shut down Windows (or Android or XWidnws...). At the end of the loop the OS looks at "raw" mouse/key events and dispatches them into appropriate application queue. It knows it by inspecting every app windows list, who is on top and thus it knows what window/app was under such and such X,Y mouse coordinate. When event gets dispatched to your app your job is to handle it very fast and look for another event in your queue (queues are bound to UI Threads/Windows).
How Timers Work?
A timer is a special kind of event that OS can keep sending to you periodically from its internal "infinite loop". OS keeps track of what apps requested to be notified and how often - when time comes, it adds a WM_TIMER(on MS Windows) into your windows queue. This way you don't block anything, but get a method in your code that gets called every X milliseconds. When you use .NET Timer class - it is just a wrapper around CreateTimer() KillTimer() (I dont recall exact func names) in Windows User APIs. .NET Timer also knows how to swallow the WM_TIMER and call a C# event/delegate for you.
I hope this helps!
Your code does nothing useful. It just starts a new background thread, which, in turn, invokes a delegate, being executed at the same UI thread, which had started... the background thread.
In other words, you can't move the label in worker thread, because moving the label brings to repainting, which can't be done from background thread.
I also had an idea of doing some work in a thread - and while this hard job
was carried out... the main-gui-form should be modified, so the user will
spot a progress.
Did some lookup and went into "delegates", "eventhandlers", and "very advanced pieces of code".
It took me some time to fix, and I came up with this very simple example. Have a look.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ProcessingUI
// You will find a form with "button1": will do some work in a seperate thread, and
// therefore you are allowed to do action in main-gui-form while this work is done,
// due to async. operation.
// While work is done in seperate thread - the main-gui-form will have a label modified...
// having the steps: 1,2,3,0.
// Also found... "button2": will do some work in same thread as gui, and
// therefore you are not allowed to do action in main-gui-form while this work is done,
// due to sync. operation (only one thread is established).
// While work is done in one-and-only-thread - the main-gui-form will have a label modified...
// having the steps: 1,2,3,0.
{
public delegate void UpdateTextDelegate();
public partial class Form1 : Form
{
public delegate void SetStatusText(string statusText);
public SetStatusText mySetStatusTextDelegate;
public Form1()
{
InitializeComponent();
mySetStatusTextDelegate = new SetStatusText(SetStatusTextMethod);
}
private void button1_Click(object sender, EventArgs e) // do work from new thread.
{
Worker w = new Worker(this);
Thread thread1 = new Thread(new ThreadStart(w.DoWork));
thread1.Start();
}
private void button2_Click(object sender, EventArgs e) // do work from local class - form is locked during 1-3 steps.
{
SetStatusTextMethod("1");
Thread.Sleep(3000);
SetStatusTextMethod("2");
Thread.Sleep(3000);
SetStatusTextMethod("3");
Thread.Sleep(3000);
SetStatusTextMethod("0");
}
public void SetStatusTextMethod(string statusText)
{
label1.Text = statusText;
label1.Refresh();
}
}
public class Worker
{
Form1 guiForm; // holds form where "control-to-be-changes" is found.
public Worker(Form1 _guiForm)
{
guiForm = _guiForm;
}
public void DoWork() // while steps are being done - form can easily be moved around... is not locked!
{
// put "1/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "1");
Thread.Sleep(3000);
// put "2/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "2");
Thread.Sleep(3000);
// put "3/3" on form.
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "3");
Thread.Sleep(3000);
guiForm.Invoke(guiForm.mySetStatusTextDelegate, "0");
}
}
}

Writing a method that reports progress

I'm writing a class that performs a certain operation in a library. But the operation is tedious, and I want to be able to find out the progress of the method inside that class so that I can use it in a WinForms application to report the progress.
I'm planning to run my class on another thread in my WinForms application and I want the class to be separated from the concerns of the WinForms application, and I don't want to bind it to anything specific other than what it does.
What would be the best way to implement a progress reporting mechanism in a library class?
Would it be a good idea to somehow have a progress variable in the class, and add an event listener to it in my WinForms application? And if it is, how can I do it?
Edit: I have used the BackgroundWorker class before, but my problem is I don't want my library class to be concerned with any of the multithreading operations. So I don't want to invoke ReportProgress in the library class, I want to (maybe) have a variable in the class that contains the current progress and I want the UI thread to somehow "subscribe" to it. I don't know if that's a good way to design it though.
Look into the BackgroundWorker class. It supports automatic marshaling across threads for progress reporting, and it has a very simple event model for this kind of support.
Edit: Given your position on using BackgroundWorker directly, what you might do is create a simple wrapper:
// Error checking elided for expository purposes.
public interface IProgressReporter
{
void ReportProgress(int progress, object status);
}
public class BackgroundWorkerProgressReporter : IProgressReporter
{
private BackgroundWorker _worker;
public BackgroundWorkerProgressReporter(BackgroundWorker worker)
{
_worker = worker;
}
public void ReportProgress(int progress, object status)
{
_worker.ReportProgress(progress, status);
}
}
Then, alter the constructor (or add a property) of the class that you want to report progress to accept an IProgressReporter. This is a form of dependency injection, and should meaningfully allow your object to report progress whilst also avoiding specific dependencies on threading libraries.
Here you can do 2 different ways I will post both options below to help point you in the right direction
You should be doing this on another thread, and then updating your UI thread from that thread. You are blocking further processing by performing this work on the UI thread.
If you can't move this code to the UI thread, then you could always call Application.DoEvents, but I strongly suggest you explore these options first:
System.ComponentModel.BackgroundWorker
System.Threading.ThreadPool
System.Threading.Thread
System.Threading.Tasks namespace
Second Alternative you could do something like this:
You'll need to get your data from one thread to the other. This can be done a couple ways...
First, your "background" thread could update some kind of "CurrentStatus" string variable that it changes as it goes along. You could then put a timer on your form that would then grab the CurrentStatus variable and update the label with it.
Second, you could simply invoke the operation from the background thread to the UI thread with a delegate using the InvokeRequired property of the label control. So for example...
private delegate void UpdateStatusDelegate(string status);
private void UpdateStatus(string status)
{
if (this.label1.InvokeRequired)
{
this.Invoke(new UpdateStatusDelegate(this.UpdateStatus), new object[] { status });
return;
}
this.label1.Text = status;
}
You can call that UpdateStatus() method from any thread (UI or background) and it will detect whether or not it needs to invoke the operation on the main UI thread (and if so, does it).
Edit: To actually set up the thread, you can do so like this:
private void StartProcessing()
{
System.Threading.Thread procThread = new System.Threading.Thread(this.Process);
procThread.Start();
}
private void Process() // this is the actual method of the thread
{
foreach (System.IO.FileInfo f in dir.GetFiles("*.txt"))
{
// Do processing
// Show progress bar
// Update Label on Form, "f.Name is done processing, now processing..."
UpdateStatus("Processing " + f.Name + "...");
}
}
Then when the user clicks the "GO" button you'll simply call StartProcessing().

Display progress bar while doing some work in C#?

I want to display a progress bar while doing some work, but that would hang the UI and the progress bar won't update.
I have a WinForm ProgressForm with a ProgressBar that will continue indefinitely in a marquee fashion.
using(ProgressForm p = new ProgressForm(this))
{
//Do Some Work
}
Now there are many ways to solve the issue, like using BeginInvoke, wait for the task to complete and call EndInvoke. Or using the BackgroundWorker or Threads.
I am having some issues with the EndInvoke, though that's not the question. The question is which is the best and the simplest way you use to handle such situations, where you have to show the user that the program is working and not unresponsive, and how do you handle that with simplest code possible that is efficient and won't leak, and can update the GUI.
Like BackgroundWorker needs to have multiple functions, declare member variables, etc. Also you need to then hold a reference to the ProgressBar Form and dispose of it.
Edit: BackgroundWorker is not the answer because it may be that I don't get the progress notification, which means there would be no call to ProgressChanged as the DoWork is a single call to an external function, but I need to keep call the Application.DoEvents(); for the progress bar to keep rotating.
The bounty is for the best code solution for this problem. I just need to call Application.DoEvents() so that the Marque progress bar will work, while the worker function works in the Main thread, and it doesn't return any progress notification. I never needed .NET magic code to report progress automatically, I just needed a better solution than :
Action<String, String> exec = DoSomethingLongAndNotReturnAnyNotification;
IAsyncResult result = exec.BeginInvoke(path, parameters, null, null);
while (!result.IsCompleted)
{
Application.DoEvents();
}
exec.EndInvoke(result);
that keeps the progress bar alive (means not freezing but refreshes the marque)
It seems to me that you are operating on at least one false assumption.
1. You don't need to raise the ProgressChanged event to have a responsive UI
In your question you say this:
BackgroundWorker is not the answer
because it may be that I don't get the
progress notification, which means
there would be no call to
ProgressChanged as the DoWork is a
single call to an external function .
. .
Actually, it does not matter whether you call the ProgressChanged event or not. The whole purpose of that event is to temporarily transfer control back to the GUI thread to make an update that somehow reflects the progress of the work being done by the BackgroundWorker. If you are simply displaying a marquee progress bar, it would actually be pointless to raise the ProgressChanged event at all. The progress bar will continue rotating as long as it is displayed because the BackgroundWorker is doing its work on a separate thread from the GUI.
(On a side note, DoWork is an event, which means that it is not just "a single call to an external function"; you can add as many handlers as you like; and each of those handlers can contain as many function calls as it likes.)
2. You don't need to call Application.DoEvents to have a responsive UI
To me it sounds like you believe that the only way for the GUI to update is by calling Application.DoEvents:
I need to keep call the
Application.DoEvents(); for the
progress bar to keep rotating.
This is not true in a multithreaded scenario; if you use a BackgroundWorker, the GUI will continue to be responsive (on its own thread) while the BackgroundWorker does whatever has been attached to its DoWork event. Below is a simple example of how this might work for you.
private void ShowProgressFormWhileBackgroundWorkerRuns() {
// this is your presumably long-running method
Action<string, string> exec = DoSomethingLongAndNotReturnAnyNotification;
ProgressForm p = new ProgressForm(this);
BackgroundWorker b = new BackgroundWorker();
// set the worker to call your long-running method
b.DoWork += (object sender, DoWorkEventArgs e) => {
exec.Invoke(path, parameters);
};
// set the worker to close your progress form when it's completed
b.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) => {
if (p != null && p.Visible) p.Close();
};
// now actually show the form
p.Show();
// this only tells your BackgroundWorker to START working;
// the current (i.e., GUI) thread will immediately continue,
// which means your progress bar will update, the window
// will continue firing button click events and all that
// good stuff
b.RunWorkerAsync();
}
3. You can't run two methods at the same time on the same thread
You say this:
I just need to call
Application.DoEvents() so that the
Marque progress bar will work, while
the worker function works in the Main
thread . . .
What you're asking for is simply not real. The "main" thread for a Windows Forms application is the GUI thread, which, if it's busy with your long-running method, is not providing visual updates. If you believe otherwise, I suspect you misunderstand what BeginInvoke does: it launches a delegate on a separate thread. In fact, the example code you have included in your question to call Application.DoEvents between exec.BeginInvoke and exec.EndInvoke is redundant; you are actually calling Application.DoEvents repeatedly from the GUI thread, which would be updating anyway. (If you found otherwise, I suspect it's because you called exec.EndInvoke right away, which blocked the current thread until the method finished.)
So yes, the answer you're looking for is to use a BackgroundWorker.
You could use BeginInvoke, but instead of calling EndInvoke from the GUI thread (which will block it if the method isn't finished), pass an AsyncCallback parameter to your BeginInvoke call (instead of just passing null), and close the progress form in your callback. Be aware, however, that if you do that, you're going to have to invoke the method that closes the progress form from the GUI thread, since otherwise you'll be trying to close a form, which is a GUI function, from a non-GUI thread. But really, all the pitfalls of using BeginInvoke/EndInvoke have already been dealt with for you with the BackgroundWorker class, even if you think it's ".NET magic code" (to me, it's just an intuitive and useful tool).
For me the easiest way is definitely to use a BackgroundWorker, which is specifically designed for this kind of task. The ProgressChanged event is perfectly fitted to update a progress bar, without worrying about cross-thread calls
There's a load of information about threading with .NET/C# on Stackoverflow, but the article that cleared up windows forms threading for me was our resident oracle, Jon Skeet's "Threading in Windows Forms".
The whole series is worth reading to brush up on your knowledge or learn from scratch.
I'm impatient, just show me some code
As far as "show me the code" goes, below is how I would do it with C# 3.5. The form contains 4 controls:
a textbox
a progressbar
2 buttons: "buttonLongTask" and "buttonAnother"
buttonAnother is there purely to demonstrate that the UI isn't blocked while the count-to-100 task is running.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonLongTask_Click(object sender, EventArgs e)
{
Thread thread = new Thread(LongTask);
thread.IsBackground = true;
thread.Start();
}
private void buttonAnother_Click(object sender, EventArgs e)
{
textBox1.Text = "Have you seen this?";
}
private void LongTask()
{
for (int i = 0; i < 100; i++)
{
Update1(i);
Thread.Sleep(500);
}
}
public void Update1(int i)
{
if (InvokeRequired)
{
this.BeginInvoke(new Action<int>(Update1), new object[] { i });
return;
}
progressBar1.Value = i;
}
}
And another example that BackgroundWorker is the right way to do it...
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
namespace SerialSample
{
public partial class Form1 : Form
{
private BackgroundWorker _BackgroundWorker;
private Random _Random;
public Form1()
{
InitializeComponent();
_ProgressBar.Style = ProgressBarStyle.Marquee;
_ProgressBar.Visible = false;
_Random = new Random();
InitializeBackgroundWorker();
}
private void InitializeBackgroundWorker()
{
_BackgroundWorker = new BackgroundWorker();
_BackgroundWorker.WorkerReportsProgress = true;
_BackgroundWorker.DoWork += (sender, e) => ((MethodInvoker)e.Argument).Invoke();
_BackgroundWorker.ProgressChanged += (sender, e) =>
{
_ProgressBar.Style = ProgressBarStyle.Continuous;
_ProgressBar.Value = e.ProgressPercentage;
};
_BackgroundWorker.RunWorkerCompleted += (sender, e) =>
{
if (_ProgressBar.Style == ProgressBarStyle.Marquee)
{
_ProgressBar.Visible = false;
}
};
}
private void buttonStart_Click(object sender, EventArgs e)
{
_BackgroundWorker.RunWorkerAsync(new MethodInvoker(() =>
{
_ProgressBar.BeginInvoke(new MethodInvoker(() => _ProgressBar.Visible = true));
for (int i = 0; i < 1000; i++)
{
Thread.Sleep(10);
_BackgroundWorker.ReportProgress(i / 10);
}
}));
}
}
}
Indeed you are on the right track. You should use another thread, and you have identified the best ways to do that. The rest is just updating the progress bar. In case you don't want to use BackgroundWorker like others have suggested, there is one trick to keep in mind. The trick is that you cannot update the progress bar from the worker thread because UI can be only manipulated from the UI thread. So you use the Invoke method. It goes something like this (fix the syntax errors yourself, I'm just writing a quick example):
class MyForm: Form
{
private void delegate UpdateDelegate(int Progress);
private void UpdateProgress(int Progress)
{
if ( this.InvokeRequired )
this.Invoke((UpdateDelegate)UpdateProgress, Progress);
else
this.MyProgressBar.Progress = Progress;
}
}
The InvokeRequired property will return true on every thread except the one that owns the form. The Invoke method will call the method on the UI thread, and will block until it completes. If you don't want to block, you can call BeginInvoke instead.
BackgroundWorker is not the answer because it may be that I don't get the progress notification...
What on earth does the fact that you're not getting progress notification have to do with the use of BackgroundWorker? If your long-running task doesn't have a reliable mechanism for reporting its progress, there's no way to reliably report its progress.
The simplest possible way to report progress of a long-running method is to run the method on the UI thread and have it report progress by updating the progress bar and then calling Application.DoEvents(). This will, technically, work. But the UI will be unresponsive between calls to Application.DoEvents(). This is the quick and dirty solution, and as Steve McConnell observes, the problem with quick and dirty solutions is that the bitterness of the dirty remains long after the sweetness of the quick is forgotten.
The next simplest way, as alluded to by another poster, is to implement a modal form that uses a BackgroundWorker to execute the long-running method. This provides a generally better user experience, and it frees you from having to solve the potentially complicated problem of what parts of your UI to leave functional while the long-running task is executing - while the modal form is open, none of the rest of your UI will respond to user actions. This is the quick and clean solution.
But it's still pretty user-hostile. It still locks up the UI while the long-running task is executing; it just does it in a pretty way. To make a user-friendly solution, you need to execute the task on another thread. The easiest way to do that is with a BackgroundWorker.
This approach opens the door to a lot of problems. It won't "leak," whatever that is supposed to mean. But whatever the long-running method is doing, it now has to do it in complete isolation from the pieces of the UI that remain enabled while it's running. And by complete, I mean complete. If the user can click anywhere with a mouse and cause some update to be made to some object that your long-running method ever looks at, you'll have problems. Any object that your long-running method uses which can raise an event is a potential road to misery.
It's that, and not getting BackgroundWorker to work properly, that's going to be the source of all of the pain.
I have to throw the simplest answer out there. You could always just implement the progress bar and have no relationship to anything of actual progress. Just start filling the bar say 1% a second, or 10% a second whatever seems similar to your action and if it fills over to start again.
This will atleast give the user the appearance of processing and make them understand to wait instead of just clicking a button and seeing nothing happen then clicking it more.
Here is another sample code to use BackgroundWorker to update ProgressBar, just add BackgroundWorker and Progressbar to your main form and use below code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Shown += new EventHandler(Form1_Shown);
// To report progress from the background worker we need to set this property
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
void Form1_Shown(object sender, EventArgs e)
{
// Start the background worker
backgroundWorker1.RunWorkerAsync();
}
// On worker thread so do our thing!
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Your background task goes here
for (int i = 0; i <= 100; i++)
{
// Report progress to 'UI' thread
backgroundWorker1.ReportProgress(i);
// Simulate long task
System.Threading.Thread.Sleep(100);
}
}
// Back on the 'UI' thread so we can update the progress bar
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
}
refrence:from codeproject
Use the BackgroundWorker component it is designed for exactly this scenario.
You can hook into its progress update events and update your progress bar. The BackgroundWorker class ensures the callbacks are marshalled to the UI thread so you don't need to worry about any of that detail either.
Reading your requirements the simplest way would be to display a mode-less form and use a standard System.Windows.Forms timer to update the progress on the mode-less form. No threads, no possible memory leaks.
As this only uses the one UI thread, you would also need to call Application.DoEvents() at certain points during your main processing to guarantee the progress bar is updated visually.
Re: Your edit.
You need a BackgroundWorker or Thread to do the work, but it must call ReportProgress() periodically to tell the UI thread what it is doing. DotNet can't magically work out how much of the work you have done, so you have to tell it (a) what the maximum progress amount you will reach is, and then (b) about 100 or so times during the process, tell it which amount you are up to. (If you report progress fewer than 100 times, the progess bar will jump in large steps. If you report more than 100 times, you will just be wasting time trying to report a finer detail than the progress bar will helpfully display)
If your UI thread can happily continue while the background worker is running, then your work is done.
However, realistically, in most situations where the progress indication needs to be running, your UI needs to be very careful to avoid a re-entrant call. e.g. If you are running a progress display while exporting data, you don't want to allow the user to start exporting data again while the export is in progress.
You can handle this in two ways:
The export operation checks to see if the background worker is running, and disabled the export option while it is already importing. This will allow the user to do anything at all in your program except exporting - this could still be dangerous if the user could (for example) edit the data that is being exported.
Run the progress bar as a "modal" display so that your program reamins "alive" during the export, but the user can't actually do anything (other than cancel) until the export completes. DotNet is rubbish at supporting this, even though it's the most common approach. In this case, you need to put the UI thread into a busy wait loop where it calls Application.DoEvents() to keep message handling running (so the progress bar will work), but you need to add a MessageFilter that only allows your application to respond to "safe" events (e.g. it would allow Paint events so your application windows continue to redraw, but it would filter out mouse and keyboard messages so that the user can't actually do anything in the proigram while the export is in progress. There are also a couple of sneaky messages you'll need to pass through to allow the window to work as normal, and figuring these out will take a few minutes - I have a list of them at work, but don't have them to hand here I'm afraid. It's all the obvious ones like NCHITTEST plus a sneaky .net one (evilly in the WM_USER range) which is vital to get this working).
The last "gotcha" with the awful dotNet progress bar is that when you finish your operation and close the progress bar you'll find that it usually exits when reporting a value like "80%". Even if you force it to 100% and then wait for about half a second, it still may not reach 100%. Arrrgh! The solution is to set the progress to 100%, then to 99%, and then back to 100% - when the progress bar is told to move forwards, it animates slowly towards the target value. But if you tell it to go "backwards", it jumps immediately to that position. So by reversing it momentarily at the end, you can get it to actually show the value you asked it to show.
If you want a "rotating" progress bar, why not set the progress bar style to "Marquee" and using a BackgroundWorker to keep the UI responsive? You won't achieve a rotating progress bar easier than using the "Marquee" - style...
We are use modal form with BackgroundWorker for such a thing.
Here is quick solution:
public class ProgressWorker<TArgument> : BackgroundWorker where TArgument : class
{
public Action<TArgument> Action { get; set; }
protected override void OnDoWork(DoWorkEventArgs e)
{
if (Action!=null)
{
Action(e.Argument as TArgument);
}
}
}
public sealed partial class ProgressDlg<TArgument> : Form where TArgument : class
{
private readonly Action<TArgument> action;
public Exception Error { get; set; }
public ProgressDlg(Action<TArgument> action)
{
if (action == null) throw new ArgumentNullException("action");
this.action = action;
//InitializeComponent();
//MaximumSize = Size;
MaximizeBox = false;
Closing += new System.ComponentModel.CancelEventHandler(ProgressDlg_Closing);
}
public string NotificationText
{
set
{
if (value!=null)
{
Invoke(new Action<string>(s => Text = value));
}
}
}
void ProgressDlg_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
FormClosingEventArgs args = (FormClosingEventArgs)e;
if (args.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
}
}
private void ProgressDlg_Load(object sender, EventArgs e)
{
}
public void RunWorker(TArgument argument)
{
System.Windows.Forms.Application.DoEvents();
using (var worker = new ProgressWorker<TArgument> {Action = action})
{
worker.RunWorkerAsync();
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
ShowDialog();
}
}
void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
Error = e.Error;
DialogResult = DialogResult.Abort;
return;
}
DialogResult = DialogResult.OK;
}
}
And how we use it:
var dlg = new ProgressDlg<string>(obj =>
{
//DoWork()
Thread.Sleep(10000);
MessageBox.Show("Background task completed "obj);
});
dlg.RunWorker("SampleValue");
if (dlg.Error != null)
{
MessageBox.Show(dlg.Error.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
dlg.Dispose();

GUI update when starting event handler class on separate thread?

We have a DLL that monitors changes in status and looks for events from a separate, purchased product. Actually, it is a ScreenPop API from Siemens, for those of you who may know what that is. I am using C#.NET 3.5 as the platform.
This API takes a looong time to initialize, so we want to use a separate thread to initialize it. Currently, we have the functionality in a class called ScreenPop. The class monitors 2 events, a status change event and a screen pop event (data that tells us who the customer is that is calling).
The way this is currently implemented doesn't work, or at least doesn't work reliably. Within the ScreenPop class, there is an initialization method where all the long-running startup code is placed. This is called from the constructor of the class, like this:
public ScreenPop( string Address, int Ext, CallbackStatusType pStatusFunc,
CallbackScreenPopType pPopFunc
)
{
CallbackStatus = pStatusFunc;
CallbackPopup = pPopupFunc;
Thread t = new Thread( StartInBackground );
t.Start();
}
In the GUI code, the func at pStatusFunc updates a status label, and the func at pPopupFunc will fire off some other code to do the screen pop - right now it just displays the data from the event.
There is a lot of glue missing, but I hope you get the point. The problem with this approach is the GUI is not updated. I know the events fire and the event handlers run, and the callback functions are getting called and they seem like they should be running, but the GUI is never updated.
So, my question is, should I abandon this in favor of a BackgroundWorker approach? Or am I just missing something in getting the GUI to update?
More info on request...
Thanks,
Dave
You can never update the GUI from a different thread - only from the UI thread, which is the one that started the application. You need to use the Control.Invoke method to run code on the UI thread. Form instance, frmMain.Invoke.
You cannot use WinForms in a multithreaded apartment, to get around this, you have to marshal over to the UI thread to perform actions on it or get results. Since you are using C#3.5, you can make use of lambdas, generics, and extension methods to make a really clean and easy to use solution.
public static class ControlExtensions
{
public static TResult InvokeEx<TControl, TResult>(this TControl control,
Func<TControl, TResult> func)
where TControl : Control
{
if (control.InvokeRequired)
{
return (TResult)control.Invoke(func, control);
}
else
{
return func(control);
}
}
}
Now you can safely and easily make changes or get values.
this.InvokeEx(f => f.label1.Text = "Hello from another thread");
new Thread(() =>
{
string formTitle = this.InvokeEx(f => f.Text); // Safely get form title
}).Start();

Categories