BackgroundWroker cross-thread operation not valid - c#

I created a backgroundworker to fill a datagirdview. The DatagridView is filled using a list which gets 2000 records from the table. I used background worker to remove the non-responsive UI.
private BackgroundWorker worker;
worker = new BackgroundWorker() { WorkerReportsProgress = true };
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var listAccGroups = vwAccVoucherDetails.ToList(); // vwAccVoucherDetails is the table containing records.
dgvBalanceSheet.DataSource = listAccGroups;
}
The error I am getting is:
Cross-thread operation not valid: Control 'dgvBalanceSheet' accessed
from a thread other than the thread it was created on.
How can I set the datagridView's datasource without getting these kind of errors?

You need to use the Completed event of BackgroundWorker:
BackgroundWorker worker = new BackgroundWorker() { WorkerReportsProgress = true };
worker.DoWork += worker_DoWork;
worker.Completed += worker_Completed;
worker.RunWorkerAsync();
void worker_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = vwAccVoucherDetails.ToList(); // vwAccVoucherDetails is the table containing records.
}
void worker_Completed(object sender, RunWorkerCompletedEventArgs e) {
dgvBalanceSheet.DataSource = e.Result;
}
Follow the steps in this tutorial for detailed instructions on how to use the BackgroundWorker class.

Use the ProgressChanged or RunWorkerCompleted callbacks on the background worker (similar to the DoWork event handling). This will then be done on the UI thread and you won't have the difficulties that show up now.

You can not access UIThread from background worker thread, in this case you can fill the grid after backgroundWorker completed, so you can add filling datagrid code into worker_completed method, but in cases you want to update UI when worker in progress, you have to implement InvokerRequired, BeginInvoke Pattern

Related

Progress Bar with Label — Unable to Update Label in ProgressChanged

I have a background worker with a long running task. The task goes through a list of files and I want to update the user with which file we are on. I have a tool strip that has a label named panel1.text. The progress bar is working however the label is not changing in my ProgressChanged method i.e. It should say Processing File1 then change to Processing File2, but it stays on the default of Processing.
private void btnProcess_Click(object sender, EventArgs e)
{
toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Maximum = 1000000000;
panel1.Text = "Processing "; // this appears properly
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(processFiles);
worker.ProgressChanged += ProgressChanged;
worker.RunWorkerAsync();
while (worker.IsBusy)
{
// the reason for this is because nothing can happen until the processing is done
toolStripProgressBar1.Increment(1);
}
// more processing
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
panel1.Text = "Processing "+ e.UserState.ToString(); <<<---- This is Not Updating panel1.Text but it evaluates properly
}
private void processFiles(object sender, EventArgs e)
{
int retVal = 0;
foreach (string fileName in listBox1.Items)
{
ProgressChangedEventArgs ea = new ProgressChangedEventArgs(1,fileName);
ProgressChanged(this, ea);
// do more processing
}
}
I would appreciate any help.
You are using the same thread, which is being blocked by another process. You need to use a Task to create a new thread and possibly use Dispatcher.BeginIvoke if the control is on the other thread. Make sure whatever Button Click, etc is happening is marked with the Async keyword as well to make it Asynchronous.
Example:
Await Task mytask = Task.Run(() =>
for(var i = 0; i < 1000; i++)
{
Label.Dispatcher.BeginInvoke( () =>
UpdateMe(int i, LabelClass/Component class/component)});
Then inside the Label Class or wherever the label is:
Public void UpdateMe(int i, LabelClass class)
{
class.label.content = Cint((i/Total)*100);
Thread.Sleep(500);
}
There are other ways to do it as well such as Binding the value to the UI, but this will give you a better understanding of why its not working and how things work with other threads.
If you want to really get a visual understanding call:
`Console.WriteLine($"Current Thread ID: System.Threading.Thread.CurrentThread.ManagedThreadId}");`
Right before you go into the Task---it will give you the main thread ID
Then inside the Task call it again...this will give you the secondary thread ID.
Then Right before the Dispatcher call:
Console.WriteLine($"Do I have access to the label on this thread? {Label.Dispatcher.CheckAccess()}";
If you have access it will display True, if not it will display False...In your case it will display false because its owned by the other thread, but you can use the Dispatcher to be able to do work on that thread while in another thread...
Also, I recommend you not use Background Worker and use Tasks instead...this explains why in depth...basically Tasks do everything Background workers do and more, have less issues and are easier to work with...
http://blog.stephencleary.com/2013/09/taskrun-vs-backgroundworker-conclusion.html
As already commented by Ivan, remove the while loop while (worker.IsBusy) as it's blocking the UI thread to process further. As well, you should enable the WorkerReportsProgress to true
worker.WorkerReportsProgress = true;
worker.ProgressChanged += ProgressChanged;
while (!worker.IsBusy)
{
worker.RunWorkerAsync();
}
Per your comment, move those later processing to BackgroundWorker.RunWorkerCompleted Event

BackgroundWorker not exiting loop

I have an BackgroundWorker:
BackgroundWorker worker;
private void Form1_Load(object sender, EventArgs e)
{
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.ProgressChanged +=
new ProgressChangedEventHandler(worker_ProgressChanged);
worker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
}
DoWork Event
void worker_DoWork(object sender, DoWorkEventArgs e)
{
int percentFinished = (int)e.Argument;
while (!worker.CancellationPending && percentFinished < 100)
{
percentFinished++;
worker.ReportProgress(percentFinished);
//here I start my operation
//operation....
//operation end
}
e.Result = percentFinished;
}
Progresschanged
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
Completed
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Asynchroner Thread kam bis zum Wert:
"+e.Result.ToString());
btnStartEnd.Text = "Starten";
}
And finally my button:
private void btnStartEnd_Click(object sender, EventArgs e)
{
if (worker.IsBusy)
{
worker.CancelAsync();
btnStartEnd.Text = "Starten";
}
else
{
if (progressBar1.Value == progressBar1.Maximum)
{
progressBar1.Value = progressBar1.Minimum;
}
worker.RunWorkerAsync(progressBar1.Value);
btnStartEnd.Text = "Stoppen";
}
}
This code works but I get a loop for my operations until the percentage is 100, so the operation starts 100 times and so takes 100 times longer.
The goal should be that the operation only starts one time and the percentage counts from 1-100.
Maybe I understand something wrong, but how does the worker know how far the operation is done? That value should be send to the progress bar for visualisation.
Normally you won’t add the loop inside the DoWork method
If you want to load for example 100 files from the file system and report the progress it could look like that:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
for(int i = 0; i < 100; i++)
{
// Load file and do something with the content
...
// Report the progress which causes the ProgressChanged event to be fired
// And update progressbar with the UI thread
worker.ReportProgress(i);
}
}
If you only have one long running operation that needs to be executed inside the DoWork method it needs to be executed asynchronously
Here is one example how you could call an action asynchronously in .NET:
Action action = () =>
{
for(int i = 0; i <100; i++)
{
Console.WriteLine(String.Format("Step {0} of long running operation", i));
Thread.Sleep(1000);
}
};
var r = action.BeginInvoke(null, null);
while(!r.IsCompleted)
{
Console.WriteLine("Waiting...");
Thread.Sleep(100);
}
However in .NET there are many more ways to do it. See for example:
http://msdn.microsoft.com/en-us/library/jj152938(v=vs.110).aspx for
Async patterns in .NET
http://msdn.microsoft.com/de-de/library/hh191443.aspx for Async
programming with await/async (new in .NET 4.5)
Maybe I understand something wrong, but how does the worker know how far the operation is done? That value should be send to the progress bar for visualisation.
The BackgroundWorker class doesn't know anything about your operations and about its progress. It's your job to determine when to report the progress.
In general the workflow for a background worker looks like this:
UI thread calls RunWorkerAsync.
DoWork event handler is called on a different thread. During the event handler you can report progress using the ReportProgress method
If you report a progress then the ProgressChanged event handler is called on the UI thread. Here you can update a progress bar for example.
When your event handler for the DoWork event exits the RunWorkerComplete event is raised.
Now why does every example for the BackgroundWorker has a for-loop? Because it's very easy to write, and measuring progress for a for-loop is trivial. Unfortunately this quite often isn't useful for different kind of operations.
If your long running operation processes N files then it's pretty obvious that you can update the progress bar after every item by 1/N. That's what the for-loop example does.
But if you only have one long running operation then you simply don't have any chance to get the progress unless the operation itself supports reporting it or if you can somehow estimate the progress.
The BackgroundWorker can't magically give a long running operation a progress bar. It only enables you to run the operation in the background.

C# Can't close form created by Background Worker

My Backgroundworker loads a new "pop-up" form, but how do I terminate background worker and the newly created form?
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BussyForm bussyForm = new BussyForm();
bussyForm.ShowDialog();
}
This has no effect:
backgroundWorker1.Dispose();
backgroundWorker1.CancelAsync();
backgroundWorker1 = null;
You shouldn't be showing a form from a non-UI thread. You should only have one UI thread, and it, and only it, should access all of your user interface controls. Your non-UI threads shouldn't access UI elements at all.
You should be showing the given busy popup from the UI thread instead.
Requesting cancellation from the BackgroundWorker, or disposing of it, won't close the form, or force the thread to stop executing, which is why your form stays open.
Instead just show your popup from the UI thread when you start the background worker, and have the BGW's completed event call Close on the form:
private void button1_Click(object sender, EventArgs args)
{
BusyForm busyForm = new BusyForm();
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += (_, e) => { busyForm.Close(); };
worker.RunWorkerAsync();
busyForm.ShowDialog();
}
CancelAsync doesn't actually abort your thread or anything like that. It sends a message to the worker thread that work should be cancelled via BackgroundWorker.CancellationPending. Your DoWork delegate that is being ran in the background must periodically check this property and handle the cancellation itself.
You call bussyForm.ShowDialog(); so you need to close this form manually.

How Run Two Operations concurrently in C#

I have a problem in my WPF app. I have a custom CircularProgressBar. When I retrieve data from database it takes a few seconds.
I would like to show the CircularProgressBar in my app while the data is retrieved.
This code runs the CircularProgressBar :
CircularProgressBar cb = new CircularProgressBar();
stk.Children.Add(cb);
ThreadStart thStart = delegate()
{
ThreadStart inv = delegate()
{
stk.Children.Remove(cb);
};
this.Dispatcher.Invoke(inv, null);
};
Thread myThread = new Thread(thStart);
myThread.Start();
in my custom class (Printer).
And where I call this window:
Printer p = new Printer();
p.Show();
//Code For retrieve Data from DataBase
p.close();
So this happens : CircularProgressBar shows for a few seconds and it not running. Where is my bug?
You can simply use background worker:
private BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//load data from database
System.Threading.Thread.Sleep(1);
worker.ReportProgress(progressbar_value);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Progress.value= progressbar_value;
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//progress completed
}
This is not how you do stuff in wpf -
use a model to populate the data from the db and than bind CircularProgressBar visibility to
the state you're in (hide it when you completed the task).
all this boilerplate code should be in xaml.
If I were you, I would simplify life by using databinding with dependencyproperties.
What are the steps to follow.
1) Create a dependency property called IsBusyProperty of type bool in your custom progressbar.
2) Register a delegate to its value change event (this is done when you create the dependency property).
3) You can now bind this IsBusyProperty to a status in your code that says hey I am busy.
4) When the value is set to true you get your progressbar to start its magic.
5) When it is set to false you stop the magic.
It is far simpler to create a control with a storyboard that rotates, so long as your ui is not locked it will rotate then simply kill it afterward.
Try this

How to handle long running "thread" in WPF?

good evening!
currently i'm developing a wpf-client for some rest-service. the communcation with the rest-service is no problem and is done in an extra assembly (communcation-interface).
basically:
i have a somehow "search"-button which executes a method. this method communicates with the service, updates some textboxes and a progress-bar (to give the user some graphic info, how far we are ...).
unfortunaly the server, which hosts the service is a bit lame, causing some severe response-time (about 4 secs). this, on the other hand, causes my wpf-application to wait, which ends up in: going black, and titeling "not responding" ...
i've already tried to put this execution in another thread, but ... it's logical that i won't get any access to the controls of my wpf-window ...
atm i'm really helpless ... can anyone give me some handeling-routine or a solution?
Your UI thread is busy waiting on a response from the web service, and isn't available to paint the screen. One good option, is push the service request off to another, non-UI thread. Look into BackgroundWorker, which was designed specifically to make this easy. It handles marshalling of cross-thread calls from non-UI to UI threads.
Roughly:
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync(arg);
...
static void bw_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = (BackgroundWorker)sender;
int arg = (int)e.Argument;
e.Result = CallWebService(arg, e);
}
static void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Increment();
}
static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label.Text = "Done: " + e.Result.ToString();
}
To access your controls from a second thread use Dispatcher.BeginInvoke:
Dispatcher.BeginInvoke(new Action(() =>
{
// Update your controls here.
}), null);
Or you can look into using BackgroundWorker.

Categories

Resources