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().
Related
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.
While maintaining some code, I discovered that we have an infinite hang-up in a background worker. The worker requires access to a script file. The original code was written to pop up a file dialog if no script file was defined, to allow the user to select one. It looks something like this:
private void bgworker_DoWork(object sender, DoWorkEventArgs e)
{
... snip ...
if (String.IsNullOrWhitespace(scriptFile))
{
scriptFile = PromptForScript();
}
... snip ...
}
private string PrompForScript()
{
string script = "";
OpenFileDialog openDialog = new OpenFileDialog();
if (openDialog.ShowDialog() == DialogResult.OK)
{
script = openDialog.FileName;
}
return script;
}
I've read up a bit about MethodInvoker, but almost all of the invoke methods require that you call them from a control. The background worker in question is running from a separate class, which doesn't extend Control. Do I use the form that calls the class with the bgworker for that? Or is there another way of interrupting the thread for user input?
It's not recommended to invoke the UI from the background worker DoWork event handler. BackgroundWorker is meant to do work on a non-UI thread to keep the UI responsive. You should ask for any file information before starting the BackgroundWorker object with RunWorkerAsync.
What you want to do is capture the SynchronizationContext on the UI thread and pass that along to the background worker. The BackgroundWorker can call Send() (synchronous, like Invoke) and Post() (asynchronous, like BeginInvoke) on the context to invoke back to the correct UI thread. That said, there is probably no need for the BackgroundWorker in this case- a regular threadpool thread would do just fine.
This (slightly modified) block of code from http://msmvps.com/blogs/manoj/archive/2005/11/03/74120.aspx should give you the general idea:
private void button1_Click(object sender, EventArgs e)
{
// Here we are on the UI thread, so SynchronizationContext.Current
// is going to be a WindowsFormsSynchronizationContext that Invokes properly
ctx = SynchronizationContext.Current;
ThreadPool.QueueUserWorkItem(
// This delegate is going to be invoked on a background thread
s => {
// This uses the context captured above to invoke
// back to the UI without the "messy" referencing
// of a particular form
ctx.Send(s2 =>
{
// Interact with your UI here- you are on the UI thread
},null);
}
);
}
If some Form kicks off a long-running process within another class that uses a BGworker, why wouldn't the form (or presenter, depending on UI architecture) handle the processing of the error state?
Perhaps, just pass back some status result (or throw a very targeted, specific exception that you can handle in the UI)?
Leave the background worker to determine if there IS an error, but leave handing the error (especially the UI portion of showing a message box) to the upper layers.
Sorry this didn't have more concrete code but it could go a lot of different ways depending on how your system is architected.
Well, the Form class has an Invoke method, so passing the form instance to the background working class should work.
I have a class with a Socket, listens to clients to receive data. When receive new data I want to call an event (if implemented) but as you know every connection has its own thread so the event will run at that thread and you know the rest. you can not work with form controls.
How do I call the event (or invoke it). I'm really new to thread and network programing so I appreciate any example.
public class HVremotechooser
{
public delegate void NewOrder(Order order);
public event NewOrder nOrder;
//... (code elided)
public void ReceiveCallback(IAsyncResult AsyncCall) // new connection of client
{
//... (code elided)
if (nOrder != null)
nOrder(Order); // calling the event "nOrder"
//... (code elided)
}
}
thank you.
If you want to update your form from a non-UI thread, you have to invoke the action. What I normally do is the following:
private void LongRunningBackgroundThread() {
// lots of work
...
// Update my form
InvokeIfRequired(() => {
...update form...
}
}
private static void InvokeIfRequired(Action a) {
if (control.InvokeRequired) {
control.Invoke(a);
} else {
a();
}
}
See here and here
I ran into a similar problem with a Silverlight application that I was working on last week, and used the Dispatcher.BeginInvoke method. With Windows forms, it looks like it might be easier to use Control.BeginInvoke instead (although I believe that either should work): http://msdn.microsoft.com/en-us/library/system.windows.forms.control.begininvoke.aspx
You can use the typical marshling operations like Invoke or BeginInvoke to inject the execution of a delegate onto the UI thread. Just pass an instance of ISynchronizeInvoke or SynchronizationContext to your class to facilitate the marshaling.
However, I would not do that in your case. Since, presumably anyway, these callbacks are occuring because of socket events it is possible likely they are coming in hard and heavy. You definitely do not want to slam your UI thread with all of that activity. Instead, package up all of the pertinent data and put into a collection that the UI thread can then poll for using a System.Windows.Forms.Timer on a more reasonable interval.
I rip on these marshling operations all of the time. They are way overrated and overused. Remember, there are two general methods of sharing data and signaling between UI and worker threads.
Push method via Invoke or BeginInvoke in the worker thread
Pull method via System.Windows.Forms.Timer in the UI thread
The pull method can be, and often is, more elegant.
UI Threading is a UI concern. In my opinion you shouldn't worry about invoking to the ui thread in this code. Rather the consumer of the event should do the Invoke or whatever other threading stuff they happen to need to do. That way if the UI person needs to change their strategy (e.g. by using a timer) your non-UI related code wont need to change.
I have some code which is been running by a backgroundworker I'd like some specific code which shows some GUI to run in the main thread context (2 reasons 1. it should be blocking 2.I know it's problematic to handle gui controls from a background worker)
I raise an event pass the class and listen to the event in the mainForm from there I check if invoke required and reinvoke. then call the public method of the instance I want to run in the main thread. I have a few questions:
is there any problem to handle data member which are created in the backgoundworker context from the main thread - for both reading and chaning valuse
is there any design pattern for such issue? Idealy I'd like to run any delegate- any return value and a few genric parameters- as func built in delegate- that is problematic because It means if I want to support up to 3 parameters with or without return values I'll have to have 6 events and 6 listeners which actually do the same - does anyone have an idea of how to do this correct?
Thanks!
I just wrote this for a similar question.
I use threads for these kind of stuff.
somewhere in my code:
// Definition
private static Thread TH;
....
// When process starts
TH = new Thread(new ThreadStart(Splash_MyCallBack));
TH.Start();
....
// This method starts the form that shows progress and other data
static private void Splash_MyCallBack()
{
frmLoading FL;
FL = new frmLoading();
FL.ShowDialog();
} /* Splash_MyCallBack*/
// Your process calls Splash_Stop when it is done.
static public void Splash_Stop()
{
TH.Abort();
} /* Splash_Stop*/
frmLoading performs the visual stuff, while in the background I have a very processor-intensive task.
My process reports to an interface its progress. frmLoading implements that interface so it is aware of it and can show whaever it is needed (2 progress bars in my case)
Tha only catch is, frmLoading must have this in the constructor:
Control.CheckForIllegalCrossThreadCalls= false;
which may be risky in some scenarios (not my case).
I think that the fact that the main process calls an interface to update progress, and the real interface consumes that is a pattern.
Hope this helps, I can add more stuff if you like.
Regards,
To answer your first question:
There shouldn't be any problems handling data created in the background worker. I've done it in a couple of applications and not had any issues.
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();