Cross Thread operation detected? - c#

I am working on application that uses BackgroundWorker Thread. I have a button click event on which I'm doing following things
btnLocate_Click(Object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync(lstNumbers.CheckedItems[0].ToString());
}
In the Background Worker do work event I'm adding some values in globally defined ObservableCollection like this
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
try
{
lock (locker)
{
_RecData.Add(new RecNumberData
{
// Some Values
});
}
}
In BackgroundWorker complete event I'm setting this collection as data source for grid and start a timer that create a new BackgroundWorker and do the same job again.
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
grid.DataSource = RecData;
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
bw.RunWorkerAsync(lstNumbers.CheckedItems[0].ToString());
}
Now the code runs fine when first time BackgroundWorker runs. But when second time it runs after Timer ticking event, Exception raised at line Cross Thread Opeartion Detected.
_RecData.Add(new RecNumberData
{
// Some Values
});
What could be the cause?

Speculation without seeing more code, but I would suspect that:
When you are adding values to the collection the first time through, the collection isn't bound to the UI => no problem
The second time through it is bound to the UI (in the first complete event), so your background worker is attempting to update the UI.
The solution might be to create a second observable collection for your second BackgroundWorker. Or defer adding the results to the collection until the Completed event.

You are modifying a GUI object from outside the GUI thread. You shouldn't do that.
By setting RecData as the data source of a GUI object, every change to RecData will trigger a notification that will change the GUI object. After binding RecData you are changing RecData, and therefore your GUI object, from the background worker thread. Objects that are databound to a GUI object must not be modified by any thread other than the GUI thread.
You can use Control.BeginInvoke to enqueue an action on the GUI thread.

You're adding data to a variable that is used as a datasource to a graphical component (grid), you need to tell us what UI technology you're using (WPF? WInform?).
What's happening is you're doing something on a thread that isn't the one the UI runs on, and most UI technologies do not allow things that affect them running on another thread, there are mechanisms to work around this but they depend on the tech.
Most of the time you'll want to register the code that changes data the UI reads to run on a dispatcher (it will be queued and run on the main thread as soon as time is available)

Related

How can I stop a long-running subroutine in my Form with a Button?

I have a Console app that displays a WinForms Form.
In the Form, the user clicks button 1 and it runs a long subroutine. I want to have a button 2 that can kill the subroutine at any point. However, the UI freezes when I click button 1 until the subroutine has finished. How can I get the UI to not freeze?
Your long-running code is blocking the UI thread, so you can no longer click the second button, nor interact with the UI in any way until the code is finished executing.
You'll need to move your long-running code to a separate thread. There are various (and newer) ways of doing this, but one way is the BackgroundWorker. It's pretty easy to learn, and wraps some nice functionality, like cancelling the thread.
Here's a short WinForms app to demonstrate. You have to explicitly enable the ability for the thread to be cancelled. In this example, the while loop continues indefinitely, but checks every 100ms to see if there's a request for it to be cancelled. When you click the second button, the cancellation request is sent, and the thread ends.
public partial class Form1 : Form
{
private BackgroundWorker bg;
public Form1()
{
InitializeComponent();
bg = new BackgroundWorker
{
WorkerSupportsCancellation = true
};
bg.DoWork += (sender, args) =>
{
while (true)
{
Thread.Sleep(100);
if (bg.CancellationPending)
break;
}
MessageBox.Show("Done!");
};
}
private void button1_Click(object sender, EventArgs e)
{
bg.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
bg.CancelAsync();
}
}
Following up on chouaib's comment, another nice thing about using the BackgroundWorker in a WinForms environment is that you can drop and drop it onto your designer, similar to a Menu, Timer, etc. You can then access its members in the "properties" panel, setting "WorkerSupportsCancellation" to true, subscribing to events, etc.
From your comment:
"is there a way to run this background process and be able to update the main userform? I keep getting the "cross-thread operation not valid control accessed from a thread other than the..." I want to run the long-running background operation, and have it update the main UI with text in a label (like a percentage of its progress)"
If you want to update the UI while the thread is running, you should do that from the ProgressChanged event. First, enable that option and subscribe to the event:
bg.WorkerReportsProgress = true;
bg.ProgressChanged += bg_ProgressChanged;
Then call ReportProgress() when you want to update the UI. You could pass back a percentage complete and some text, for instance:
bg.ReportProgress(50, "Almost there...");
Finally, update the UI from inside the event:
void bg_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var message = e.UserState.ToString();
var percent = e.ProgressPercentage;
lblStatus.Text = message + " " + percent;
}
You need to make it multithreaded as suggested in the comments. The older way of doing this was manage your own thread. Then along came the background worker (cheap and easy). Now a days you have other options such as the Task Library.
Remember - anything the runs on the UI thread prevents the UI from sending and receiving events until that operation is finished.
Look into the BackgroundWorker component

How to Pause without Blocking the UI

I have a click event which causes the phone to vibrate once a button is clicked. This generally works great, except sometimes the vibration doesnt stop until I completely close the application. I would like to give the application time to complete its vibration and then continue with the rest of the method, but I do not want to block the UI at all. How might I accomplish this
MainPage.xaml.cs
void newButton_Click(object sender, EventArgs e)
{
//Button vibration
if (Settings.EnableVibration.Value) //boolean flag to tell whether to vibrate or not
{
VibrateController.Default.Start(TimeSpan.FromMilliseconds(100));
//place vibration stop here?
}
this.NavigationService.Navigate(new uri("/NewPage.xaml", UriKind.Relate));
}
I have already tried VibrationController.Default.Stop(); but this completely eliminates the vibration all together. Is there a way to simply wait until after the vibration has completed to then navigate to a new page, or do whatever other action the method should perform? Any reccomendations or advice on this implementation or other suggestions?
You can use asynchrony to prevent blocking the UI. Rather than actually blocking the UI thread you need to schedule an action to happen again 100ms from now. Adding a continutation to a Task.Delay call can do just that:
void newButton_Click(object sender, EventArgs e)
{
Action navigate = () =>
this.NavigationService.Navigate(new uri("/NewPage.xaml", UriKind.Relate));
if (Settings.EnableVibration.Value) //boolean flag to tell whether to vibrate or not
{
VibrateController.Default.Start();
Task.Delay(100).ContinueWith(t =>
{
VibrationController.Default.Stop();
navigate();
});
}
else
navigate();
}
.NET wraps this functionality up conveniently in the BackgroundWorker class.
private void SomeMethod()
{
// Create backgroundworker
BackgroundWorker bw = new BackgroundWorker();
// Attach event handler
bw.DoWork += bw_DoWork;
// Run Worker
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
// Do background stuff here
}
It also has support for progress updates and triggers an event on completion, as far as I know, this functionality extends to windows phone. All of this is covered in the MSDN article.
I would guess what you want to do is call vibrate in the BackgroundWorker, and you can listen for the RunWorkerCompletedEvent which will fire when it is finished. Also, you can happily pause this "thread" and it will not interfere with the UI.

C# Backgroundworker keep on running DoWork

Below is my coding:
Form2 msgForm;
private void button3_Click_1(object sender, EventArgs e)
{
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
msgForm = new Form2();
try
{
bw.RunWorkerAsync();
msgForm.ShowDialog();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// Coding that transmit protocol and will last around 2 minutes.
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
msgForm.Close();
}
I use the background worker method everytime I click a button to transmit protocols that last around 2 minutes. During transmission, the From2 will show 'Please wait'.
But I have some problem using this coding. The problem is like, when I click the button the first time, it will transmit the protocol once. After that I click again which is the second time, it transmit the protocol twice. After that I click again which is the third time, it transmit the protocol 3 times.... And so on. The times of protocol of transmit increase each time I click the button.
Aren't that it will only run once the coding in void bw_DoWork everytime I click the button?
Is there something wrong with my coding?
You're appending an additional handler every time you click, and then it's run along with everything you added before, which stays where it is (because the object is still there, you're re-using it).
To solve this, you need to either:
Move the declaration of the background worker inside the method (so it's new every time, with only one DoWork handler
Like this:
private void button3_Click_1(object sender, EventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
// rest of your code
}
Move the .DoWork += ... which appends the handler in the constructor of the class
It mostly depends on if you use that worker somewhere else.
It looks like a new worker starts on every click.
To avoid this behaviour, check if the worker is busy before starting it again.
try
{
if (!bw.IsBusy)
bw.RunWorkerAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
And you can disable the button while the backgroundworker does its job, by button3.Enabled = false; and re-enable it in the bw_RunWorkerCompleted method, letting the user understand he has to wait and cannot click again until the process is finished.
First make sure that your lists/Collections are clear before transmit codes.
Then Use BreakPoint in your source code and remember that your BackgroundWorker can't run twice or more because you use ShowDialog.
Check if the worker is busy:
if (!bw.IsBusy)
bw.RunWorkerAsync();
I would also disable the button and change the text to for example 'Running' while the process is being excecuted.
Use the bw_RunWorkerCompleted method event call to then re-enable the button and change the text. This method runs on the same thread as the UI so there are no cross thread issues.
On your design, why are you showing another form for the 'Please wait' notification? I would suggest updating a label on your existing form either before the async process starts (so not cross thread UI issues) or if you need to update after then you can use the following:
lblNotify.Invoke(new Action(() => lblNotify.Text = #"Please wait"));
The above will then allow you to run your request on the main thread.

Gui freeezing when using threading

I am stuck and was hoping someone could help me.
I have made a class/gui with a loading bar set to marquee so that when a task is being carried out i could display it to the user.
In one of my gui classes, in the constructor on the first line i am making a new instance of this class and then doing
LoadingBar bar = new LoadingBar();
Thread thread = new Thread(bar.Show);
thread.Start();
However, even tho the main programme thread is going off doing some more intensive stuff, this gui still seems to freeze, even if i use backround worker.
Is there anything wrong with the approach i have mentioned and if so what do i need to change?
Thanks
You need to reverse your method. The GUI needs to stay in the main thread while the work is done in a "worker thread" (typically a BackGroundWorker). Then the worker reports back to the GUI which then updates.
You'd better do the opposite. Make your intensive work in the thread (or a background worker), and show the wait screen in the main application thread.
You need to use a BackgroundWorker. Drag one onto your form, click backgroundWorker1 and set the WorkerReportsProgress property to True
Then goto the events (via the properties window) and attach handlers for
DoWork, this is where all the work that is represented by the progress bar. You will "report progress" via this and the background worker will make sure ProgressChanged is called on the UI thread.
ProgressChanged, this is where you update the UI based on progress and state data reported to the method
DoWork event looks something like this
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var userState = new StateClass();
while (working)
{
// TODO: do work here
// update the state surrounding this task via userState
userState.property = "some status";
// report the progress so that backgroundWorker1_ProgressChanged gets called
this.backgroundWorker1.ReportProgress(percentComplete, userState);
}
}
ProgressChanged event looks like this
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// e.UserState contains the state data you passed to ReportProgress,
// you have to cast it to the right type though, since its of type object
var userState = (StateClass)e.UserState;
int progress = e.ProgressPercentage;
// TODO: report progress to the UI with the above variables
}
Now all you have to do is tell the background worker to do work by calling
this.backgroundWorker1.RunWorkerAsync()
I don't think you're doing all your work within the background worker or the GUI wouldn't be freezing. We'll need to see more of your code or you'll need to have another look at background worker examples to see what could be wrong.
EDIT
Add a call to System.Threading.Thread.Sleep(1); after your report progress call

How to improve UI in .Net Compact Framework, C#?

I have some WinForms in my application.
On some form , I am loading other forms which have lot of data to show.
So when I click a button to load these forms they take, 9-10 seconds to load,
I wana improve this. I have heard about Background thread but don't know how to use this.
Means on the button click what should i do so that I can see my form imidiately and the processing is done by background thread ?
Please guide me so that I can improve my UI.
Getting the form to show immediately, should be fairly simple and you don't need a background thread to do it.
My guess is that you are retrieving all that data prior to loading the form or you are retrieving the data on the same thread that loads the form. You should be able to call the form with the data it needs to retrieve the rest of the data (such as ID's).
Then you just need to find a way for the data processing to happen on a thread other than the thread that creates the form.
This may not be the most elegant solution, but it gets the job done. It uses a System.Windows.Forms.Timer
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
//fill controls with data
}
If you have several sets of data that you are retrieving, you may want to consider setting up a queue of tasks to retrieve each set.
Any way that you do it. Make sure to disable any buttons(or other ui) that could cause errors if the data is not loaded.
Edit: It should be noted that winform controls don't always play nice with threads. The nice thing about the System.Windows.Forms.Timer (as opposed to System.Timers.Timer)is that it designed to play nice with controls.
BackgroundWorker are easy to use. Mainly you subscribe to DoWork event, this is where you'd put your work that will be done in a seperate thread. And you also subscribe to RunWorkerCompleted event. This will get raised when your DoWork has completed.
In this method you'd simply bind the data fetched by the DoWork method.
You can use the DoWork eventarg to pass data between DoWork and RunWorkerCompleted.
You can also cancel the work and also the you can use the RunWorkerCompleted eventArg to get the data from the DoWork and also inspect the Error property for exception handling.
IF you have lots of data, you can use a batching process, to get data per batch and display them into your UI. You can show how many total records will be fetch and the progress of it 100 out of 1000 have been loaded, 200 out of 1000 have been loaded etc, to keep the user informed. You could also provide a Cancel option so the user can stop the fetch.
In your button click you'd simply call myBackgroundWorker.RunWorkerAsync().
this will call your DoWork Handler which will be done in a seperate thread not the main UI Thread, so your UI will not FREEZE during the background worker DoWork.
The backgroundworker also support report progress.
If your problem is the main form Freezing, what you need is to collect the data on a separated Thread than the Main form is running.
To achieve that, you can do something like this:
BackgroundWorker worker;
public Form1()
{
InitializeComponent();
worker = new BackgroundWorker();
}
private void button1_Click(object sender, EventArgs e)
{
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//Collect data and open your second form here;
}

Categories

Resources