I keep getting
Cross-thread operation not valid: Control 'keyholderTxt' accessed from a thread other than the thread it was created on.
on various controls on various forms in a project, and I have googled it and found lot's of responses about how to access stuff from various threads, but as far as I know, i'm not using any other threads in my project, and to change the hundreds of possible places in the code would be unmanageable.
It never used to happen, only since I added various code that seems unrelated. I include a sample of places where I get the errors below, but it has occurred in so many places all over the solution.
keyholderTxt.Text = "Keyholders Currently In:\r\n \r\n Nibley 1: + keyholders";
or this, a better example, as you can see everything that happends from the form loading until the error:
private void Identification_Load(object sender, System.EventArgs e)
{
_Timer.Interval = 1000;
_Timer.Tick += new EventHandler(_Timer_Tick);
_Timer.Start();
txtIdentify.Text = string.Empty;
rightIndex = null;
SendMessage(Action.SendMessage, "Place your finger on the reader.");
if (!_sender.OpenReader())
{
this.Close();
}
if (!_sender.StartCaptureAsync(this.OnCaptured))
{
this.Close();
}
}
void _Timer_Tick(object sender, EventArgs e)
{
this.theTime.Text = DateTime.Now.ToString();
}
private void OnCaptured(CaptureResult captureResult)
{
txtIdentify.Clear();
//other stuff after the cross thread error
}
Can things like not closing datareaders cause this kind of error?
I am using a Windows Forms Application.
I suspect the culprit is this:
if (!_sender.StartCaptureAsync(this.OnCaptured))
I don't know the API you're using, but based on the name, I think the callback method (OnCaptured) is called on a worker thread, not the UI thread. So you need to use Invoke to perform the action on the UI thread:
private void OnCaptured(CaptureResult captureResult)
{
if (InvokeRequired)
{
Invoke(new System.Action(() => OnCaptured(captureResult)));
return;
}
txtIdentify.Clear();
// ...
}
Okay, scratch this. I see you're using System.Windows.Forms.Timer which, as the comment below mentions, already executes on the UI thread. I was thinking you were using System.Timers.Timer.
Wrong answer
The timer callback is executing on a threadpool thread. You can make it execute on the UI thread by setting the SynchronizingObject:
_Timer.Interval = 1000;
_Timer.Tick += new EventHandler(_Timer_Tick);
_Timer.SynchronizingObject = this;
_Timer.Start();
Have you checked the thread panel in VS?
The callback from _Timer (void _Timer_Tick(object sender, EventArgs e))is occurring on a background thread. Make sure you use a System.Windows.Forms.Timer (assuming you are using windows forms) if you want the callback to be on the UI thread.
As commenters have suggested. Check the thread window in your debugger to check what thread the exception is occurring on.
Alternatively, for windows forms, try this
void _Timer_Tick(object sender, EventArgs e)
{
this.BeginInvoke(new Action(() => this.theTime.Text = DateTime.Now.ToString()));
}
And for WPF, try this
void _Timer_Tick(object sender, EventArgs e)
{
this.Dispatcher.BeginInvoke(new Action(() => this.theTime.Text = DateTime.Now.ToString()));
}
And if this is not a control or window and you are in WPF
void _Timer_Tick(object sender, EventArgs e)
{
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() => this.theTime.Text = DateTime.Now.ToString()));
}
Related
why I can't close this from it gave me an error message (Cross-thread operation not valid: Control 'Form4' accessed from a thread other than the thread it was created on)
my form code;
System.Timers.Timer t = new System.Timers.Timer();
private void Form4_Load(object sender, EventArgs e)
{ myFunction2();}
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Elapsed += T_Elapsed;
t.Start();
t.AutoReset = false;
}
private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
myFunction();
t.Stop();
t.Enabled = false;
this.Close();
}
private void myFunction()
{
var form6 = new Form6();
//form6.Closed += (s, args) => this.Close();
form6.ShowDialog();}
Edit
I get help from a friend to change this in my code but still, the from4 open and form6 open much time.
private System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Tick += T_Elapsed;
t.Start();
}
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
this.Invoke((new Action(() =>
this.Close();
})));
}
private void myFunction()
{
Form6 form6 = new Form6();
form6.ShowDialog();}
Winforms has an "owning-thread" model.
What does that mean?
This model prevents you from accessing an UI component from another thread, not the one which created it.
Why?
Because GUI components are not thread-safe. and should not be, since they'll be much slower. So, WinForms throws an exception like that at you when you try to access a GUI component from another thread - not the owned thread.
But why does this happen toyou?
Because System.Timers.Timer executes its callback in its own thread, which isn't the thread that created the GUI (the main thread of the app). So, you can't access from its callback to any GUI component.
What's the solution?
You can access an GUI component from another thread by a tool called a Dispatcher. But if all you want is a simple timer, you have nicer solution.
Simply use System.Windows.Forms.Timer instead of System.Timers.Timer. This timer is specific to WinForms, and handles all the black work with the dispatcher for you. (Note: WPF has System.Windows.Threading.DispatcherTimer for the same purpose).
But, there's one pitfall: this timer has not AutoReset property. So, you should remove the event by hand after one run, like:
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
t.Stop();
this.Close();
}
Since you're closing the window, this is not really needed, but for safety...
Also, note that you don't need both Stop() and Enabled = false together, they are identical (I personally prefer Stop(), I think it's more readable).
In your example (with AutoReset) you didn't need Stop() at all - AutoReset = false run the callback only one time.
Edit:
Although it isn't needed in your case, I append an explanation about "how to use the dispatcher".
Each WinForms' form has a Dispatcher, and some methods related to it. The most important are Invoke() and BeginInvoke() (two overloaded versions, I'm talking about the first which takes System.Delegate).
These methods enable you two access GUI components from not-owning thread, only from the method passed as parameter (in most cases, you must cast it to System.Delegate first).
The difference is, that Invoke() returns only after the method called, while BeginInvoke() is asynchronous; it returns immediately.
So, you can rewrite you code as follows:
private System.Timers.Timer t = new System.Timers.Timer();
public Form1()
{
InitializeComponent();
t.Elapsed += T_Elapsed;
t.Interval = int.Parse(textBox1.Text);
t.AutoReset = false;
t.Start();
}
private void T_Elapsed(object sender, EventArgs e)
{
this.Invoke((Action)(() => // You can use `BeginInvoke()` as well
{
this.Close();
}));
// Or
// this.Invoke(new Action(() => // You can use `BeginInvoke()` as well
// {
// this.Close();
// }));
}
Note: Never put long-running tasks inside Invoke() or BeginInvoke()! since they're executed in the owning thread - not in the called thread, they'll freeze the GUI - it's much easier to not use threads at all... Put the calculations in the thread, and call these methods only to update the GUI!
Edit 2:
After I saw what you did with my answer, I was shocked... It seems you even didn't read it! You chose both the solutions: the winforms timer (the good), and the dispatcher (the bas, in this case)! simplify you Tick event so:
private void T_Elapsed(object sender, EventArgs e)
{
myFunction();
Close();
}
Also, in your myFunction(), you show your second form in modal form. That say, that the method won't return after the second form is closed. See What's the difference between Show(), ShowDialog() and Application.Run() functions? for more details. I think you want to show your second form modeless.
private System.Windows.Forms.Timer t = new System.Windows.Forms.Timer();
private void T_Elapsed(object sender, EventArgs e)
{
if (true)
{
myFunction();
t.Enabled = false;
t.Stop();
}
}
private void myFunction2()
{
t.Interval = int.Parse(textBox1.Text);
t.Tick += T_Elapsed;
t.Start();
}
private void myFunction()
{
t.Enabled = false;
t.Stop();
this.Hide();
Form6 form6 = new Form6();
form6.ShowDialog();}
I have a thread which calls one of the methods, now this method executes a query which can take a very long time possibly 40 minutes or so to complete,
I want to give user a a choice to be able to cancel this operation (meaning stop the thread and stop the query to release database).
I should mention that I am developing WPF Application using .net 4.5, SQL SERVER DB and C#.
You should use backgroundworker, it is exactly what you want.
Eather drag and drop it from the toolbox or create it in code - behind. It supports Cancellation, reports progress, notifies when complete and know if it is running or not.
Here is an example.
void method(){
BackgroundWorker worker = new BackgroundWorker();
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
worker.ProgressChanged += worker_ProgressChanged;
worker.DoWork += worker_DoWork;
worker.WorkerSupportsCancellation = true;
if(!worker.IsBusy)
{
worker.RunWorkerAsync();
}
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
//do whatever needs to be done on the other thread here.
object argument = e.Argument; //if passed argument in RunWorkerAsync().
object result = new object();
e.Result = result;
//after making worker global, you can report progress like so:
worker.ReportProgress(50); //you can also pass a userState, which can be any object, to show some data already.
}
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//you can update a progress bar in here
int progress = e.ProgressPercentage;
}
void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//when done
}
void CancelTheTask()
{
if (worker.IsBusy)
{
//make worker global first, but then
worker.CancelAsync();
}
}
A important things to look at: Never use resources in the DoWork method that are not created inside it. Thus pass things you need in the background worker as Arguments. And things that are created by the backgroundworker should not be set to a global variable ether, pass by result.
When cancelling, RunWorkCompleted will also be fired. Now the query to the database is already being executed, so that is still running, even when your application lost all resources to it.
To cancel that, we would need to know how you execute the query, like #S.Akbari mentioned is one way. Entity Framework 6 also supports cancellation.
For that: check this when using Queryable
here is another example
Or this solution without Entity Framework.
Using Task Parallel Library (TPL) you can use the Task Cancellation pattern.
When you have your Thread blocked on waiting for the query, it's useless for stopping anything.
Make sure the SqlConnection of the query is accessible from your UI and Close it. Abandon the Thread, it will terminate (with an error you've got to suppress).
If the UI thread is doing a Long-time operation it won't be able to process
UI requests. This is also known as Not Responding.
Use ThreadPool like this:
CancellationTokenSource ct;//instantiate it before ThreadPool.QueueUserWorkItem line
private void operation_Click(object sender, RoutedEventArgs e)
{
ct = new CancellationTokenSource();
ThreadPool.QueueUserWorkItem(_ =>
{
var result = LongTimeOperation();//set the operation in another thread so that the UI thread is kept responding
//use the Dispatcher to "return" to the UI thread
Dispatcher.BeginInvoke(new Action(() =>
{
//Use result for example : Label1.Text = result.ToString();
}));
});
}
To give user a choice to be able to cancel the operation use CancellationTokenSource like this:
private void cancel_Click(object sender, RoutedEventArgs e)
{
if (ct != null)
{
ct.Cancel();
ct= null;
}
}
Note: in LongTimeOperation() you must have one more parameter of type CancellationToken
private float LongTimeOperation(CancellationToken ct)
{
if (ct.IsCancellationRequested)
return -1;
....
....
}
This link is useful about Cancellation in Managed Threads.
this is a common problem.But in WPF and WinForm, i'd like to use BackGroundWorker. See Here
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.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
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)
{
if (comboBox15.Text == "")
{
//MessageBox.Show("Please select Printer ID.", "Status", MessageBoxButtons.OK, MessageBoxIcon.Error);
//return;
}
// Coding that transmit protocol and will last around 2 minutes.
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
msgForm.Close();
}
When I run this background worker coding, there's an error stating "Cross-thread operation not valid: Control 'comboBox15' accessed from a thread other than the thread it was created on."
How do I solve this problem guys?
You can use Invoke:
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.comboBox15.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate {if (comboBox15.Text == ""){// What you want to do}});
}
else
{
if (comboBox15.Text == "")
{
}
}
also read the following:
http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspx
Anonymous method in Invoke call
You can't UI elements from a non-UI-thread. Ideally, provide the relevant information to the background worker before it starts, e.g.
string text = combo15.Text;
bw.DoWork += (sender, args) => TransmitStuff(combo15.Text, args);
...
void TransmitStuff(string printerId, DoWorkEventArgs e)
{
...
}
If you can use .NET 4.5 and C# 5, you could use an async method to quite possibly make all of this easier... but I realize that's unlikely to be an option for you.
EDIT: While you can use Invoke, that ends up being quite messy - and you've got potentially inconsistent state. I generally think it's tidier to work out all the state you need before you start the long-running operation, validate it all, and then hand it to the operation. If you need to update the UI during the operation, you can use the BackgroundWorker progress facilities.
In BackgroundWorker, when we call any user controls its problem. Please use this property in Window Form Load event:
CheckForIllegalCrossThreadCalls = false;
You can only access gui controls from your main thread.
Move the
if (comboBox15.Text == "")
part to button3_click
You can get round it by passing the value such as below.
private void Dowork()
{
backgroundWorker1.RunWorkerAsync(comboBox1.Text);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
String selected = (String)e.Argument;
if (String.IsNullOrEmpty(selected)) return;
//do stuff
}
EDIT #1: I have placed worker.RunWorkerAsync() within my timer loop and my application does not shut down anymore. Although nothing seems to happen now.
For performance reasons i need to replace DispatcherTimers with a other timer that runs in a different thread. There are to much delays / freezes so DispatcherTimer is no longer a option.
I am having problems to actually update my GUI thread, my application always seems to shut down without any warnings / errors.
I have mainly been trying to experiment with BackGroundWorker in attempt to solve my problem. Everything results in a shut down of my application when i launch it.
Some code examples would be greatly apperciated.
Old code dispatcher code:
public void InitializeDispatcherTimerWeging()
{
timerWegingen = new DispatcherTimer();
timerWegingen.Tick += new EventHandler(timerWegingen_Tick);
timerWegingen.Interval = new TimeSpan(0, 0, Convert.ToInt16(minKorteStilstand));
timerWegingen.Start();
}
private void timerWegingen_Tick(object sender, EventArgs e)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
Every 5 seconds the DisplayWegingInfo() and Calculate method should be called upon.
The GUI updates happen in the Calculate method. There a button gets created dynamically and added to a observerableCollection.
Button creation (short version):
public void CreateRegistrationButton()
{
InitializeDispatcherTimerStilstand();
RegistrationButton btn = new RegistrationButton(GlobalObservableCol.regBtns.Count.ToString());
btn.RegistrationCount = GlobalObservableCol.regBtnCount;
btn.Title = "btnRegistration" + GlobalObservableCol.regBtnCount;
btn.BeginStilstand = btn.Time;
GlobalObservableCol.regBtns.Add(btn);
GlobalObservableCol.regBtnCount++;
btn.DuurStilstand = String.Format("{0:D2}:{1:D2}:{2:D2}", 0, 0, 0);
}
New code using threading timer that runs in a different thread then the GUI
public void InitializeDispatcherTimerWeging()
{
worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(Worker_DoWork);
worker.RunWorkerAsync();
}
void Worker_DoWork(object sender, DoWorkEventArgs e)
{
TimerCallback callback = MyTimerCallBack;
timerWegingen = new Timer(callback);
timerWegingen.Change(0, 5000);
}
private void MyTimerCallBack(object state)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
I timer runs in a separate thread then the GUI thread (that dispatcherTimer uses). But i cannot seem to be able to send this update to the UI thread itself so the updates get actually implemented in the UI.
The button gets refilled with new values every 1 sec trough a other timer. "DuurStilstand" is a dependency property
private void FillDuurStilstandRegistrationBtn()
{
TimeSpan tsSec = TimeSpan.FromSeconds(stopWatch.Elapsed.Seconds);
TimeSpan tsMin = TimeSpan.FromMinutes(stopWatch.Elapsed.Minutes);
TimeSpan tsHour = TimeSpan.FromMinutes(stopWatch.Elapsed.Hours);
if (GlobalObservableCol.regBtns.Count >= 1
&& GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].StopWatchActive == true)
{
GlobalObservableCol.regBtns[GlobalObservableCol.regBtns.Count - 1].DuurStilstand =
String.Format("{0:D2}:{1:D2}:{2:D2}", tsHour.Hours, tsMin.Minutes, tsSec.Seconds);
}
}
Would i need to use the invoke from Dispatcher in the above method? If so how exactly?
Not sure how to call the ui thread after initializing the doWork method of the BackGroundWorker, my application keeps shutting down after right after start up.
I have tried using Dispatcher.BeginInvoke in several methods but all failed so far. At the moment i have no clue how to implement it.
All the above code is written in a separate c# class.
Best Regards,
Jackz
When I ran my sample of your code, the DisplayWegingInfo() was throwing an exception trying to access UI components. We need to call Invoke() from the Timer thread to update the UI. See DisplayWegingInfo() below. Note: this assumes that CaculateTimeBetweenWegingen() does not interact with the UI.
void Worker_DoWork(object sender, DoWorkEventArgs e)
{
TimerCallback callback = MyTimerCallBack;
timerWegingen = new System.Threading.Timer(callback);
timerWegingen.Change(0, 3000);
}
private void MyTimerCallBack(object state)
{
DisplayWegingInfo();
CaculateTimeBetweenWegingen();
}
private void DisplayWegingInfo()
{
if (this.InvokeRequired)
{
this.Invoke(new Action(DisplayWegingInfo));
return;
}
// at this point, we are on the UI thread, and can update the GUI elements
this.label1.Text = DateTime.Now.ToString();
}
private void CaculateTimeBetweenWegingen()
{
Thread.Sleep(1000);
}
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.