C# Can't close form created by Background Worker - c#

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.

Related

BackgroundWroker cross-thread operation not valid

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

Changing a form from a separate background worker

I have a form which when loaded starts a looping background worker which gets data from a usb device every half a second.
Once the program receives a new piece of data from the usb device it runs a function.
The _Dowork function has
while (true)
{
portConnection.Write("?");
Thread.Sleep(50);
}
I then have a separate routine that runs when data is received
private void portConnection_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
}
This is the routine that cannot then set values on the original form as the function is apparently on a separate thread.
How can I make this routine able to influence the original form?
Try something like this:
private void InvokeIfRequired(Control target, Delegate methodToInvoke)
{
if (target.InvokeRequired)
target.Invoke(methodToInvoke);
else
methodToInvoke.DynamicInvoke();
}
Call the method in your ProcessStatsReceived and in the methodToInvoke do your stuff...
You can use it like this in the ProccessStatusReceived:
InvokeIfRequired(this, new MethodInvoker(delegate() { this.lblStatus.Text = (string)e.Data; }));
The report progress part of BackgroundWorker is made for this.
This will make the DoWork method able to call a method on the GUI thread.
See this msdn article for details.
In short, the needed parts are:
Bind the progress changed handler:
bw.ProgressChanged += bw_ProgressChanged;
Set the BW to allow progress reporting:
bw.WorkerReportsProgress = true;
Implement the progress change method:
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//your logic to be executed on the GUI thread here
}
Then call it from DoWork like this:
bw.ReportProgress( progressPercentage, userState )
progressPercentage and userState can be user to transfer data from the background thread to the ProgressChanged method on the GUI thread.

BackgroundWorker with Dispatcher doesn't seem to do anything

I am trying to update an ObservableCollection that is data bound to the UI. I know that to do this I need to use Dispatcher and BeginvInvoke(), and to make it so that the UI doesn't freeze up when I do so, using a BackgroundWorker is a good way to go about it. In any event, I have all this, compiled and running, but nothing happens. I need to update the UI every 2 minutes or so, so I am also using a DispatcherTimer
This works, because DispatcherTimer is part of Dispatcher, but freezes the UI:
DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();
private void dispTimer_Tick(object sender, EventArgs e)
{
PartialEmployees.Clear();
}
So, using the BackgroundWorker I pieced together this:
DispatcherTimer dispTimer = new DispatcherTimer();
dispTimer.Tick += dispTimer_Tick;
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();
private void dispTimer_Tick(object sender, EventArgs e)
{
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += DoWork;
_worker.RunWorkerAsync();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
Dispatcher.CurrentDispatcher.BeginInvoke( new Action(()=>
{
PartialEmployees.Clear();
}));
}
But nothing happens to the UI. What am I missing/not doing correctly?
You have two problems:
When you use Dispatcher.CurrentDispatcher from the background thread, it is getting the background thread's Dispatcher, not the UI thread's Dispatcher.
From your description I gather that your PartialEmployees.Clear() method takes significant time to execute and you want to avoid locking the UI thread during the execution. However, having a BackgroundWorker invoke PartialEmployees.Clear() on your UI thread will have the same effect as using the DispatcherTimer, so you need a different solution than the one you are going for.
If you only want to fix the Dispatcher.CurrentDispatcher problem, just store the current Dispatcher in a local variable like this:
private void dispTimer_Tick(object sender, EventArgs e)
{
var uiDispatcher = Dispatcher.CurrentDispatcher;
BackgroundWorker _worker = new BackgroundWorker();
_worker.DoWork += (sender, e) =>
uiDispatcher.BeginInvoke(new Action(() =>
{
PartialEmployees.Clear();
}));
_worker.RunWorkerAsync();
}
This will cause your UI change to work but it will still lock up the UI during the change, exactly as if you had not used BackgroundWorker. The reason for this is:
The DispatcherTimer fires, executing on the UI thread. All it does (dispTimer_Tick) is start a BackgroundWorker and then exit.
The BackgroundWorker executes on its own therad. All it does is schedule a Dispatcher callback and then exit.
The Dispatcher callback executes on the UI thread again. It calls PartialEmployees.Clear() which takes a while, locking up your UI thread while it executes.
So your behavior is the same as if the DispatcherTimer callback had called PartialEmployees.Clear() directly: In each case the time-consuming operation is executed on the UI thread.
The reason for the lockup is that any time you do a large piece of work on the UI thread you will get a momentary lockup while it runs. The solution is to break your work into smaller portions and do them one at a time, either from a DispatcherTimer or a BackgroundWorker. In your case, examine the code for PartialEmployees.Clear() to see if it can be done incrementally.
The problem here is that you're using the method Dispatcher.CurrentDispatcher from the back ground thread. What you need is the Dispatcher instance for the UI thread.
_worker.DoWork += delegate { DoWork(Dispatcher.CurrentDispatcher); };
...
private void DoWork(Dispatcher dispatcher) {
dispatcher.BeginInvoke(new Action(() => {
PartialEmployees.Clear();
});
}
I dont think you need the background work, as BeginInvoke on the Dispatcher runs on a Threadpool thread.
something like this should work, and is more succinct
DispatcherTimer dispTimer = new DispatcherTimer
{Interval = TimeSpan.FromSeconds(45)};
dispTimer.Tick += (o,e) => Dispatcher.CurrentDispatcher
.BeginInvoke((Action)PartialEmployees.Clear);
dispTimer.Start();

C# threading issue

To play a bit with threading, delegates and backgroundworkers, I'm putting together a few small applications, I'm having a bit of trouble with one of them.
I've a Windows form, with a textbox, a button and a richttext.
When I press the button, the text in the textbox is used as a paramter to instantiate a class, like this:
public partial class Form1 : Form
{
private BackgroundWorker backgroundWorker;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(worker_DoWork);
backgroundWorker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.AppendText(response);
Application.DoEvents();
}
});
}).Start();
}
void OnUpdateTicker(string msg)
{
new Thread((ThreadStart)delegate()
{
this.BeginInvoke((ThreadStart)delegate()
{
label4.Text = msg;
Application.DoEvents();
});
}).Start();
}
}
When debugging I run into a 'textBox1.Lines' threw an exception of type 'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException'
Any tips on how to solve this problem?
First, there is no need to create new threads inside DoWork; the whole idea with the BackgroundWorker is that DoWork is executed on a separate thread. Second, since DoWork is executed on a separate thread and UI controls can be modified only on the UI thread, you need to invoke those updates correctly. So, a rewritten version of worker_DoWork could look like this:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (string line in textBox1.Lines)
{
Dig digger = new Dig(line, textBox1.Text);
digger.DomainChecked += new Dig.DomainCheckedHandler(OnUpdateTicker);
string response = digger.GetAllInfo();
richTextBox1.Invoke((Action) delegate { richTextBox1.AppendText(response); });
}
}
Note how the code does not explicitly spawn any new threads, and also how the AppendText method call is done through a Control.Invoke call, forcing it to execute on the UI thread.
The main reason is that the textbox is not owned by the background thread.
Your UI thread owns all the UI objects, and you're spinning up a background thread when a button is pressed. That background thread should not have access to any UI objects.
If you want the value of the textbox to be used, you'll need to pass it to your background thread another way.
Have a look here for an explanation (and solution).
You can only update controls on the main thread from the main thread itself, unless you explicitly tell your program that it's ok to do, by using the .Invoke method of the control.
From: http://www.albahari.com/threading/part3.aspx
Control.Invoke
In a multi-threaded Windows Forms application, it's illegal to call a method or property on a control from any thread other than the one that created it. All cross-thread calls must be explicitly marshalled to the thread that created the control (usually the main thread), using the Control.Invoke or Control.BeginInvoke method. One cannot rely on automatic marshalling because it takes place too late – only when execution gets well into unmanaged code, by which time plenty of internal .NET code may already have run on the "wrong" thread – code which is not thread-safe.

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