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();}
Related
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()));
}
I am trying to delay events in my method by using a timer, however i do not necessarily understand how to use a timer to wait.
I set up my timer to be 2 seconds, but when i run this code the last call runs without a 2 second delay.
Timer timer = new Timer();
timer.Tick += new EventHandler(timer_Tick); // Everytime timer ticks, timer_Tick will be called
timer.Interval = (1000) * (2); // Timer will tick evert second
timer.Enabled = true; // Enable the timer
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
label1.Text = "second";
}
So when i click my button, it immediately shows label1 as "second", as opposed to changing to "first", waiting 2 seconds, then changing to "second". I have read lots of threads here about using timers instead of thread.sleep, but i cannot seem to find/figure out how to actually implement that.
If you're using C# 5.0 await makes this much easier:
private async void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
await Task.Delay(2000);
label1.Text = "second";
}
timer.Start() just starts the timer but immediately returns while the timer is running in the background. So between setting the label text to first and to second there is nearly no pause. What you want to do is wait for the timer to tick and only then update the label again:
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
label1.Text = "second";
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
}
Btw. you should not set timer.Enabled to true, you are already starting the timer using timer.Start().
As mentioned in the comments, you could put the timer creation into a method, like this (note: this is untested):
public void Delayed(int delay, Action action)
{
Timer timer = new Timer();
timer.Interval = delay;
timer.Tick += (s, e) => {
action();
timer.Stop();
};
timer.Start();
}
And then you could just use it like this:
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
Delayed(2000, () => label1.Text = "second");
}
Tergiver’s follow-up
Does using Delayed contain a memory leak (reference leak)?
Subscribing to an event always creates a two-way reference.
In this case timer.Tick gets a reference to an anonymous function (lambda). That function lifts a local variable timer, though it's a reference, not a value, and contains a reference to the passed in Action delegate. That delegate is going to contain a reference to label1, an instance member of the Form. So is there a circular reference from the Timer to the Form?
I don't know the answer, I'm finding it a bit difficult to reason about. Because I don't know, I would remove the use of the lambda in Delayed, making it a proper method and having it, in addition to stopping the timer (which is the sender parameter of the method), also remove the event.
Usually lambdas do not cause problems for the garbage collection. In this case, the timer instance only exists locally and the reference in the lambda does not prevent the garbage collection to collect the instances (see also this question).
I actually tested this again using the .NET Memory Profiler. The timer objects were collected just fine, and no leaking happened. The profiler did give me a warning that there are instances that “[…] have been garbage collected without being properly disposed” though. Removing the event handler in itself (by keeping a reference to it) did not fix that though. Changing the captured timer reference to (Timer)s did not change that either.
What did help—obviously—was to call a timer.Dispose() in the event handler after stopping the timer, but I’d argue if that is actually necessary. I don’t think the profiler warning/note is that critical.
If all you're trying to do is change the text when the timer ticks, would you not be better off putting...
label1.Text = "second";
...In the timer tick, either before or after you change the timer to enabled = false;
Like so;
void timer_Tick(object sender, EventArgs e)
{
timer.Stop();
label1.Text = "second";
}
private void button1_Click(object sender, EventArgs e)
{
label1.Text = "first";
timer.Start();
}
private bool Delay(int millisecond)
{
Stopwatch sw = new Stopwatch();
sw.Start();
bool flag = false;
while (!flag)
{
if (sw.ElapsedMilliseconds > millisecond)
{
flag = true;
}
}
sw.Stop();
return true;
}
bool del = Delay(1000);
I'm using a BackgroundWorker to periodically check a hardware switch. Due to it is connected via a slow RS485 network, I have to delay the next status update.
On switch Status change I want to update an OK/nOK Picture Box. This is realized as a green OK pictureBox over a nOK pictureBox. No real work is done here.
For expandability I decided to use the Backgroundworker. Finally I want to have a hidden worker, which
provides globally the Status of three switches and
updates on StatusChange the PictureBoxes.
Problem description
Once the BackgroundWorker is started, it works as expected. However the GUI freezes.
What did I try?
The MSDN BackgroundWorker Class Note 1
says, that GUI should be updated via ProgressChanged. I tried to raise this Event by Worker_Switch.ReportProgress(fakeProgress++) and failed. The PictureBox wasn't updated anymore.
Snippet from designer
this.Worker_Switch = new System.ComponentModel.BackgroundWorker();
//
// Worker_Switch
//
this.Worker_Switch.WorkerSupportsCancellation = true;
this.Worker_Switch.DoWork += new System.ComponentModel.DoWorkEventHandler(this.Worker_Switch_DoWork);
Snippet from Main Form
delegate void SetEventCallback(object sender, DoWorkEventArgs e); // Threadsafe calls for DoWork
private void btnBackgroundworker_Click(object sender, EventArgs e)
{
if (!Worker_Switch.IsBusy)
{
Worker_Switch.RunWorkerAsync();
}
}
private void Worker_Switch_DoWork(object sender, DoWorkEventArgs e)
{
// Worker Thread has no permission to change PictureBox "pictureBoxSwitchrightOK"
// Therefore this method calls itsself in the MainThread, if necessary.
while (!Worker_Switch.CancellationPending)
{
if (this.pictureBoxSwitchrightOK.InvokeRequired) // Worker Thread
{
System.Threading.Thread.Sleep(400);
SetEventCallback myCall = new SetEventCallback(Worker_Switch_DoWork);
this.Invoke(myCall, new object[] { sender, e });
}
else // Main Thread
{
// Turns OK Picture Box invisible, if nOk State (Switch pushed)
pictureBoxSwitchrightOK.Visible = SwitchOK("right"); // true: OK (green)
this.Refresh();
}
}
private bool SwitchOK(string rightOrLeft) // select one of the switches
{ (...)} // gets hardware switch status
Edit: Special Thanks to laszlokiss88 (3 possibilities) and JMK (for simplicity with System.Windows.Forms Timer from toolbox)
This alternative from Toolbox also worked:
this.timer_Switch.Enabled = true;
this.timer_Switch.Interval = 400;
this.timer_Switch.Tick += new System.EventHandler(this.timer_Switch_Tick);
private void timer_Switch_Tick(object sender, EventArgs e)
{
motorSwitchControl.Init(); // globally available Switch status
SwitchRight = SwitchOK("right");
SwitchRightOK.Visible = SwitchRight;
SwitchLeft = SwitchOK("left"); // globally available Switch status
SwitchLeftOK.Visible = SwitchLeft;
SwitchAllOK = SwitchRight & SwitchLeft;
this.Refresh();
}
a) Is it correct, that the Sleep() actually happens in the Worker Thread?
- no Main Thread
b) What is going wrong, if I manipulate user interface objects in DoWork? (Contrary to MSDN Note)
- works in Main Thread?
c) What is the correct way to periodically update a PictureBox? DoWork, ProgressChanged, RunWorkerCompleted...?
- Three possibilities from laszlokiss88 answer.
You can update the UI from the DoWork event via the Dispatcher, or Control.Begininvoke(winforms), or you can do it via the ProgressChanged event of the BackgroundWorker:
public MainWindow()
{
InitializeComponent();
var bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerAsync();
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// You are in the main thread
// Update the UI here
string data = (string)e.UserState;
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// You are in a worker thread
(sender as BackgroundWorker).ReportProgress(0, "right");
}
For a start you should almost never have a need to put an active background thead to sleep. I am also not sure why you are constructing/defining the delegate this way, try some thing like
public delegate void UpdatePictureBox();
myDelegate = new UpdatePictureBox(UpdatePictureboxMethod);
then you have a method UpdatePictureBoxMethod
private void UpdatePictureBoxMethod()
{
this.pictureBox1.Image = Properties.Resources.SomeImage;
}
or something simalar, where you pass in the image to update to.
Alternatively you could use the (bgWorker as BackgroundWorker).ReportProgress(progress, object); method. So from the background thread you call
(bgWorker as BackgroundWorker).ReportProgress(progressBarValue, infoBall);
where here class IfoBall will hold all your important information
class InfoBall
{
public int nProgressBar { get; set; }
public int nMaxProgressBar { get; set; }
public Image image { get; set; }
}
then you can pass this object back to the UI thread and do your updates
void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// On UI thread.
InfoBall someBall = (InfoBall)e.UserState;
this.pictureBox1.Image = someBall.image;
// etc...
}
I hope this helps.
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
}
I have a timer that needs to not process its elapsed event handler at the same time. But processing one Elapsed event may interfere with others. I implemented the below solution, but something feels wrong; it seems like either I should be using the timer differently or using another object within the threading space. The timer seemed to fit best because I do need to periodically check for a status, but sometimes checking will take longer than my interval. Is this the best way to approach this?
// member variable
private static readonly object timerLock = new object();
private bool found = false;
// elsewhere
timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
timer.Elapsed = Timer_OnElapsed;
timer.Start();
public void Timer_OnElapsed(object sender, ElapsedEventArgs e)
{
lock(timerLock)
{
if (!found)
{
found = LookForItWhichMightTakeALongTime();
}
}
}
You could set AutoReset to false, then explicitly reset the timer after you are done handling it. Of course, how you handle it really depends on how you expect the timer to operate. Doing it this way would allow your timer to drift away from the actual specified interval (as would stopping and restarting). Your mechanism would allow each interval to fire and be handled but it may result in a backlog of unhandled events that are handled now where near the expiration of the timer that cause the handler to be invoked.
timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds;
timer.Elapsed += Timer_OnElapsed;
timer.AutoReset = false;
timer.Start();
public void Timer_OnElapsed(object sender, ElapsedEventArgs e)
{
if (!found)
{
found = LookForItWhichMightTakeALongTime();
}
timer.Start();
}
I usually stop the timer while processing it, enter a try/finally block, and resume the timer when done.
If LookForItWhichMightTakeALongTime() is going to take a long time, I would suggest not using a System.Windows.Forms.Timer because doing so will lock up your UI thread and the user may kill your application thinking that it has frozen.
What you could use is a BackgroundWorker (along with a Timer if so desired).
public class MyForm : Form
{
private BackgroundWorker backgroundWorker = new BackgroundWorker();
public MyForm()
{
InitializeComponents();
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.RunWorkerCompleted +=
backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync();
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = LookForItWhichMightTakeALongTime();
}
private void backgroundWorker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
found = e.Result as MyClass;
}
}
And you can call RunWorkerAsync() from anywhere you want to, even from a Timer if you want. And just make sure to check if the BackgroundWorker is running already since calling RunWorkerAsync() when it's running will throw an exception.
private void timer_Tick(object sender, EventArgs e)
{
if (!backgroundWorker.IsBusy)
backgroundWorker.RunWorkerAsync();
}
timer.enabled = false
or
timer.stop();
and
timer.enabled = true
or
timer.start();
I use the System.Threading.Timer like so
class Class1
{
static Timer timer = new Timer(DoSomething,null,TimeSpan.FromMinutes(1),TimeSpan.FromMinutes(1));
private static void DoSomething(object state)
{
timer = null; // stop timer
// do some long stuff here
timer = new Timer(DoSomething, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
}
}