How to use BackgroundWorker in C# - c#

How to use BackgroundWorker in C#?
Actually i'm performing an operation of filling a PDF-Form from method called fill(). It takes more time to show up the result into pdfviewer, so I decided to show up a 'processing image' using a backgroundworker, and tried using it but failing to achieve it
here is my code snippet :
private void bgwLoadFile_DoWork(object sender, DoWorkEventArgs e)
{
this.Invoke((MethodInvoker)delegate()
{
????
});
}
private void bgwLoadFile_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
}
else if (e.Error != null)
{
}
else
{
picLoading.SendToBack();
}
}
Fill method is called when button FILL is been clicked
private void btnFill_Click(object sender, EventArgs e)
{
if (btnFill.Text == "Fill")
{
bgwLoadFile.RunWorkerAsync();
picloading.BringToFront();
Fill();
}
wat statement should i need to add in DoWork method , if i tried to add FILL() fill is been called twice ...
can any one help me out
Thanks

Add Fill(); to your bgwLoadFile_DoWork and remove it from btnFill_Click
Just a side-note you'll probably want to call your picLoading.SendToBack(); outside of that 'else' as if you error or cancel it will stay there.

So let's try to find some answers:
The method worker_DoWork() will be executed within another thread. By calling within that method this.Invoke() you're going to pass the call back to the gui thread, which makes the usage of the background worker useless. Instead within the worker method you have to call the method that needs some time and doesn't interact with the gui. If this called method produces any result (e.g. has a return value) you should write this information into the variable e.Result.
The method worker_RunWorkerCompleted() will be called within the gui thread again. Allowing you to take the result and let it somehow interact with the gui. Due to the fact, that this method will be executed on the gui thread it should be quite simple (or fast) in its doing otherwise your gui is going to freeze again.
So given these informations lets clean up your code:
private void btnFill_Click(object sender, EventArgs e)
{
if (btnFill.Text == "Fill")
{
// Update the gui for the user
// and start our long running task
// (disable buttons etc, cause the
// user is still able to click them!).
picloading.BringToFront();
bgwLoadFile.RunWorkerAsync();
}
}
private void bgwLoadFile_DoWork(object sender, DoWorkEventArgs e)
{
// Let's call the long running task
// and wait for it's finish.
Fill();
}
private void bgwLoadFile_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// We're back in gui thread.
// So let us show some results to the user.
if (e.Cancelled)
{
// To support cancellation, the long running
// method has to check some kind of cancel
// flag (boolean field) to allow fast exit of it.
labelMessage.Text = "Operation was cancelled.";
}
else if (e.Error != null)
{
labelMessage.Text = e.Error.Message;
}
// Hide the picture to allow the user
// to access the gui again.
// (re-enable buttons again, etc.)
picLoading.SendToBack();
}

Related

How to start a process only after one process has completed?

I have 2 functions I created which does specific works.
I call these functions on timers. But before function 1 completes, function 2 starts processing. How can I wait till 1 completes before starting 2.
Here is my code:
private void btnrun_Click(object sender, EventArgs e)
{
try
{
_bat.ShowDialog();
StrBatchNumber = _bat.GlobalBatchNumber;
DialogResult _diaresult = _bat.DialogResult;
if (_diaresult == DialogResult.OK)
{
if (StrBatchNumber == "")
{
MessageBox.Show("Please enter a batch number", "Batch Number Missing", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
btnstartrobo.PerformClick();
btnautostart.PerformClick();
}
}
}
catch(Exception)
{
}
}
System.Windows.Forms.Timer tmrfirst = new System.Windows.Forms.Timer();
private void btnautostart_Click(object sender, EventArgs e)
{
tmrfirst.Interval = 1000;
tmrfirst.Tick += new EventHandler(tmrfirst_Tick);
tmrfirst.Start();
}
private void tmrfirst_Tick(object sender, EventArgs e)
{
btnchecksignal.PerformClick();
Thread.Sleep(100);
if (textBox8.Text.Contains("+1"))
{
button14.PerformClick();
Thread.Sleep(20);
button42.PerformClick();
Thread.Sleep(300);
btnautostart.PerformClick();
}
}
I tried giving thread.sleep in between, but it doesn't work. How can I fix this? Please help.
The reason why your solution is not working correctly, is because both operations run in the same thread. Therefore your Thread.Sleep(100) is making both functions wait, not making the order change but delaying the complete program execution.
The easiest solution to solve this would be Multithreading (see the documentation on how to implement another thread here and how to start it running a function here)
Like this you can add the new Thread to the end of your first method or insert it into a helper method to run it only after method 1 has completed. If you call the same function several times, maybe check if the thread already exists before creating a new one or simply close it every time your second function is finished.

correct way to re-call backgroundworker

As in title, I have some bgw I want to call on every button press.
Is this code correct ?
private static BackgroundWorker bgw = null;
private void bttn_Click(...)
{
if(!bgw.IsBusy)
doSomeWorkInBg();
else
MessageBox.Show("Slow down a bit");
}
private void doSomeWorkInBg()
{
if (bgw == null)
{
bgw = new BackgroundWorker();
bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
}
bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
// do some work that takes time
}
Or maybe I should add RunWorkerCompleted event with "bgw = null;" code or something like that?
You should avoid making your bgw static, because you are using it in non-static context.
When I need to re-run the worker frequently based on the UI event, this is the construct that I usually use:
bool ShouldRunWorkedASAP;
private void bttn_Click(...){
ShouldRunWorkedASAP=true;
if (!bgw.IsBusy) bgw.RunWorkerAsync();
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
while (ShouldRunWorkedASAP) {
ShouldRunWorkedASAP=false;
// do some work that takes time
}
}
This assumes that the worked is instantiated in the class constructor. Basically this code sets the flag that the worker should run ASAP whenever the UI event occurred, then tries to run the worker. If it is already running - then the while() loop inside the worker implementation will schedule the job for the next run as soon as it completes.
This code does not ensure that the worker will run exactly the number of times the user presses the button, not sure if in your case this is required or not.

C# Threading and upgrading GUI

I've this situation:
Form "Menu" where you can click "New" and an insert form (with showdialog) will appear. If you insert data, I make an INSERT query on the database and I close this form.
After showDialog() statement, I have a lot of methods who performs a lot of operations in a sequential way (MUCH IMPORTANT) and sometimes update a dataGridView.
After all this computation, I simulate press of "new button" in order to allow user to insert a new item.
This is an example:
private void buttonNew_Click(object sender, EventArgs e)
{
DialogResult dr = new DialogResult();
InsertForm form = new InsertForm();
dr = form.ShowDialog();
if (dr == System.Windows.Forms.DialogResult.OK)
{
Method1();
Method2();
if (dataGrid2.RowCount > 0)
{
Method3();
Method4();
Method5();
Method6();
Method7();
Method8();
}
bNew.PerformClick();
}
}
The problem is that the "New" form (bNew.PerfomClick()) appear after a 2-3 seconds and I can't wait so much time. So I tried to create a method who include Method1 to Method8, run it in a new Thread and execut bNew.PerfomClick(), but this doesn't works because a lot of my methods update a datagridView.
Is there a way to solve this problems?
Sorry for my bad english.
------------UPDATE------------------
Now I'm trying this code:
delegate string OperazioniDelegate();
private string Operazioni()
{
if (!InvokeRequired)
{
Method1();
Method2();
............
}
else
Invoke(new OperazioniDelegate(Operazioni));
return "";
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Operazioni();
}
In this way, new insert form is showed instantly but his UI is blocked until the backgroundWorker End work..
Assuming you are running your long running operation in a seperate thread, which updates the UI elements (datagridview in your case), it is bound to experience cross thread exception, because the context in which background thread runs is different from that of UI thread. Either use InvokeRequired as given below:
delegate void valueDelegate(string value);
private void SetValue(string value)
{
if (someControl.InvokeRequired)
{
someControl.Invoke(new valueDelegate(SetValue),value);
}
else
{
someControl.Text = value;
}
}
or use BackGroundWorker class, which will do automatic marshalling of calls from background thread to Ui thread. Use of Background worker is given in this link http://www.albahari.com/threading/part3.aspx#_BackgroundWorker

MessageBox.Show called from backgound worker with the handle of the main UI thread

I have this code:
public void Blah(IWin32Window _this)
{
for (int i = 0; i < item_quantity; i++)
{
try { File.Delete(item[0, i]); }
catch (Exception ex)
{
if (MessageBox.Show(_this, String.Format("Error while accessing {0}\n{1}"
, item[0, i], ex.Message), "Error", MessageBoxButtons.RetryCancel
, MessageBoxIcon.Error) == DialogResult.Retry)
{ i--; }
}
}
}
...and this code in the main UI thread:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
AnotherClass.Blah(this);
}
When I execute this code, I get the unsafe cross-thread exception. What's the safe way to do this operation?
What's the safe way to do this operation?
There is no real safe way to do this. The message box pops out of nowhere, without any direct connection to a command that the user gave. One failure mode is that the user continues working with your UI, clicking the mouse or pressing the space bar. And your message box pops up a millisecond before he clicked the mouse or pressed a key. He'll never see the message.
So something was supposed to be done, it didn't get done and the user is completely unaware of it. Not a good thing. You'll need to doctor your UI so this condition can never occur. Clearly that will require that you do error reporting a different way than by using a temporary message box. Many possible alternatives of course, could be as simple as a Label that reports state. StatusStrip is good for this.
The actual exception is a bogus one. It is triggered by the built-in diagnostics that checks that code uses UI in a thread-safe way. The underlying winapi call is GetParent(), one of the very few user32 Windows functions that can safely be called, and used, from a worker thread. The only legitimate reason I know where using Control.CheckForIllegalCrossThreadCalls to work around the problem is okay. But fix the real problem instead.
I'm not condoning the design, but you can pass in the Form to Blah() and then Invoke() against the referenced form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
{
button1.Enabled = false;
backgroundWorker.RunWorkerAsync();
}
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
SomeClass AnotherClass = new SomeClass();
AnotherClass.Blah(this);
}
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
button1.Enabled = true;
MessageBox.Show("Done!");
}
}
public class SomeClass
{
public void Blah(Form frm)
{
int item_quantity = 5;
for (int i = 0; i < item_quantity; i++)
{
try
{
//File.Delete(item[0, i]);
Console.WriteLine("i = " + i.ToString());
throw new Exception("duh");
}
catch (Exception ex)
{
frm.Invoke(new Action(() =>
{
DialogResult result = MessageBox.Show(frm, String.Format("Error while accessing {0}\n{1}", "something", ex.Message), "Error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error);
if (result == DialogResult.Retry)
{
i--;
}
}));
}
}
}
}
You are trying to do UI work on a background thread, hence the cross-thread exception. RunWorkerCompletedEventArgs has a property called Error that will hold any exception that gets thrown by the RunWorkerAsync delegate. Set up a handler for RunWorkerCompleted on your BackgroundWorker and check if the Error property has a value. If it does, prompt the MessageBox in the handler because you will be on the UI thread at that point. Call the BackgroundWorker's RunWorkerAsync method again on the DialogResult.Retry scenario.
(You will probably have to tweak your BackgroundWorker and AnotherClass.Blah to take in the value of i to prime your loop condition for that second call to your BackgroundWorker. The DoWorkEventArgs has a property called Argument that you can use to pass in that value.)
You need to execute UI code like this when calling it from another thread:
// must use invoke because the timer event is running on a separate thread
this.Invoke(new Action(() =>
{
MessageBox.Show("Message");
}));

Call Method From BackgroundWorker

I hate that my first question seems to have been answered many times, but I'm still having a tough time getting my head around how to call a method using BackgroundWorker.
I'm processing a very large text file using a series of classes and methods. The entire process is kicked off after the user selects a tool strip item. Sequentially, it goes like this:
User selects the tool strip item
User selects a file to be processed via a dialog box
The action starts
I think I can wrap everything into BackgroundWorker from the moment the user pops the initial dialog box, but what I'd like to do for now is just put the method where all the heavy lifting is done into its own instance of BackGroundWorker. I'll add a ProgressBar, too, but I think I can handle that if I can just get the BackgroundWorker process rolling.
From the top (pseudocode used for example purposes. Much omitted for brevity):
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
string fileName = openSingleFile.FileName;
processFile(fileName);
}
static public void processFile(string fileName)
{
// many vars/loops exist but not shown
foreach (data in bigData)
{
processItem(stringA, stringB); // <-- this method is where the expensive work is done
x++;
}
}
I've created an instance of BackgroundWorker...:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Things go here
}
...and I've tried too many things to list, so I've gone back to the beginning for the presentation above.
If I'm understanding BackgroundWorker, I'll need to do the following:
Replace processItem(stringA, stringB) in the above code with something like:
backgroundWorker1.RunWorkerAsync(processItem(stringA, stringB));
...and then do some type of DoWork call?
...and then do some type of RunWorkerCompleted call?
Not sure why my brain is freezing, but I'm embarrassed at the amount of time I've spent on this with no result. Any help would be greatly appreciated. Without StackOverflow, I would have been DOA a long time ago.
FYI: I've referenced other SO posts, MSDN, and DotNetPerls examples. I'm just missing something conceptually, I suppose.
Replace processItem(stringA, stringB) in the above code with something like...
No, that's how you got in trouble. You most definitely want to move the processFile() call to the worker. There is no perceivable benefit from running processItem() in a worker, at least not in the snippet you posted. And doing so is difficult, it would require starting more than one worker. One for each item. Having a lot of workers that each do little work is not very healthy. If it is really necessary then you don't want to use BackgroundWorker, you'll want an entirely different approach with several Threads that consume packets of work from a thread-safe queue. Don't go there if you can avoid it.
The only non-trivial problem to solve is passing the string that processFile() needs. Luckily BackgroundWorker.RunWorkerAsync() has an overload that takes a single object. Pass your string. Obtain its value in your DoWork event handler, casting e.Argument back to a string. Thus:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
string path = (string)e.Argument;
processFile(path);
}
private void processToolStripMenuItem_Click(object sender, EventArgs e) {
backgroundWorker1.RunWorkerAsync(openSingleFile.FileName);
processToolStripMenuItem.Enabled = false;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
processToolStripMenuItem.Enabled = true;
}
Starting up a new background worker is an expensive operation. You don't want to be starting one for each iteration of a loop. Instead, put the entire loop inside of a single background worker's scope.
When ToolStripMenuItem_Click is run create your background worker, have processFile be what is done in the DoWork event handler.
Make sure that when doing that work you're really just doing that work, not updating the UI. You'll want to separate business logic from the user interface. If you want to update the UI with some current progress then call ReportProgress and ensure that there is an event handler to properly update the UI.
If you need to update the UI when the work is all done then you can do so in the RunWorkerCompleted event handler. If the work you are doing generates some result that is used to update the UI use the Result property of the background worker to pass it from the DoWork method to the completed handler.
BackgroundWorker bgw;
In the Load event or constructor:
bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
//bgw.WorkerSupportsCancellation = true;
bgw.DoWork += bgw_DoWork;
bgw.ProgressChanged += bgw_ProgressChanged;
bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
/
private void ToolStripMenuItem_Click(object sender, EventArgs e)
{
string fileName = openSingleFile.FileName;
bgw.RunWorkerAsync(fileName);
}
private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
string fileName = (string)e.Argument;
processFile(fileName);
}
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int Progress = e.ProgressPercentage;
//Update progressbar here
}
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Job completed
}

Categories