This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on
public void CheckUnusedTabs(string strTabToRemove)
{
TabPage tp = TaskBarRef.tabControl1.TabPages[strTabToRemove];
tp.Controls.Remove(this);
TaskBarRef.tabControl1.TabPages.Remove(tp);
}
I am trying to close a tab in the tabcontrol of windows application using the above code and i encountered the error:
Cross-thread operation not valid.
How to solve this ?
You can only make changes to WinForm controls from the master thread. You need to check whether InvokeRequired is true on the control and then Invoke the method as needed.
You can do something like this to make it work:
public void CheckUnusedTabs(string strTabToRemove)
{
if (TaskBarRef.tabControl1.InvokeRequired)
{
TaskBarRef.tabControl1.Invoke(new Action<string>(CheckUnusedTabs), strTabToRemove);
return;
}
TabPage tp = TaskBarRef.tabControl1.TabPages[strTabToRemove];
tp.Controls.Remove(this);
TaskBarRef.tabControl1.TabPages.Remove(tp);
}
call using invoke, because you're accessing the GUI thread using another thread
this.Invoke((MethodInvoker)delegate() {CheckUnusedTabs(""); });
When using threads and UI controls, in winforms, you need to use InvokeRequired to make changes to the controls.
EDIT.
added an example.
Form, with button and label.
try
private void button2_Click(object sender, EventArgs e)
{
Thread thread = new Thread(UpdateProcess);
thread.Start();
}
private void SetLabelText(string val)
{
label1.Text = val;
}
delegate void m_SetLabel(string val);
private void UpdateProcess()
{
int i = 0;
while (true)
{
if (label1.InvokeRequired)
{
m_SetLabel setLabel = SetLabelText;
Invoke(setLabel, i.ToString());
}
else
label1.Text = i.ToString();
i++;
Thread.Sleep(500);
}
}
Cross thread not valid exception is due to the UI controls being accessed from other threads than main thread.see this
http://helpprogramming.blogspot.com/2011/10/invalid-cross-thread-operation.html
Set the following variable:
CheckIllegalCrossThreadValidation = false
Related
This question already has answers here:
How to stop BackgroundWorker on Form's Closing event?
(12 answers)
Closed 6 years ago.
I tried to make a new thread using the winform application. Here is my sample code.
public static bool stop = false;
private Thread mythread(){
Thread t = new Thread(delegate() {
while(!stop){
// Something I want to process
}
});
return t;
}
private Button1_click(object sender, EventArgs e){
stop = true; // I know it doesn't work
this.Dispose();
this.Close();
}
public Main(){
InitializeComponent();
Thread thread = mythread();
thread.Start();
}
When the button1 is clicked, new thread and winform should be terminated, but new thread is still working. Is there any way to terminate the new thread?
ps: I tried to change my code reffering to the MSDN site example but it only made it more complicated.
This is a problem of visibility of variables in other threads... try this:
private static int stop = 0;
private Thread mythread(){
Thread t = new Thread(delegate() {
while(Thread.VolatileRead(ref stop) == 0){
// Something I want to process
}
});
return t;
}
private Button1_click(object sender, EventArgs e){
Thread.VolatileWrite(ref stop, 1);
this.Dispose();
this.Close();
}
public Main(){
InitializeComponent();
Thread thread = mythread();
thread.Start();
}
Notes:
Aborting threads is not recommended.
Making stop volatile should be enough in your example.
The presented code is using Thread.VolatileRead and Thread.VolatileWrite to update the variable. Using int instead of bool, and not using the volatile keyword will ease transition to Interlocked if you need it.
I recommend the series of articles Threading in C# by Joseph Albahari
In WPF, one could use something like:
Application.Current.Dispatcher.BeginInvoke(new Action(() => Form1.grid.Items.Refresh()));
to access UI functions outside of the main thread. In Winforms however, the same functionality isn't there. What would be the easiest way to access a BindingList that exists inside of my Form1 class from my "working" thread? Currently I getting the following error when trying to access "Form1.record_list":
System.InvalidOperationException: Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on.
edit: I appreciate the help so far, but I'm lost on "this.Invoke". My method in the separate thread has no "Invoke".
Here's an example of my code so far.
public static void listen(IPEndPoint server_ip)
{
Console.WriteLine("In listen");
while (true)
{
try
{
byte[] received_bytes = udp_client.Receive(ref server_ip);
string received_data = Encoding.ASCII.GetString(received_bytes);
Record record = JsonConvert.DeserializeObject<Record>(received_data);
Form1.record_list.Add(record); //This is where I assume the problem spawns
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
public partial class Form1 : Form
{
public static BindingList<Record> record_list = new BindingList<Record> { };
public static DataGridViewCellStyle style = new DataGridViewCellStyle();
public Form1()
{
InitializeComponent();
Thread thread = new Thread(SCMClient.connect);
thread.IsBackground = true;
thread.Start();
FillData();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
foreach (DataGridViewRow row in dataGridView.Rows)
{
for (var i = 0; i < row.Cells.Count; i++)
{
Console.WriteLine(row.Cells[i].Value);
if (row.Cells[i].Value as string == "OK")
{
row.Cells[i].Style.BackColor = Color.Red;
Console.WriteLine("IF WAS TRUE");
}
}
}
}
I think the specific problem here is when I add Records to the Forms1.record_list. I'm not sure how to add items to that list without causing the Cross-thread error...
You MUST access your UI from your UI Thread only. But you can use the Control.Invoke method - a Form IS a Control - to ensure your code is run from the UI Thread.
Also, you have a static BindingList, but I assume you want to display the contents of that list in an specific form. You should make the BindingList an instance member instead... or get a reference to a valid Form. The Control.Invoke method is not static.
There are several ways to do so. I would do it like so:
First, create a method in your Form class that adds the record to the list.
public void AddRecord(Record r) {
if(this.InvokeRequired) {
this.Invoke(new MethodInvoker(() => this.AddRecord(r)));
} else {
this.record_list.Add(r);
}
}
Second, you need to have a reference to the form (in the next step, that is the theForm variable).
Then, in your listener method, invoke AddRecord method instead of adding the record in your BindingList directly.
public static void listen(IPEndPoint server_ip)
{
Console.WriteLine("In listen");
while (true)
{
try
{
byte[] received_bytes = udp_client.Receive(ref server_ip);
string received_data = Encoding.ASCII.GetString(received_bytes);
Record record = JsonConvert.DeserializeObject<Record>(received_data);
theForm.AddRecord(record); // You need a Form instance.
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
The below will work out in winforms. Have tried once. The below code helps to update the label in UI thread from another thread.
string _string = "Call from another thread";
this.Invoke((MethodInvoker)delegate {
label1.Text = _string;
});
You're looking for SynchronizationContext.Current, which works with both WinForms and WPF.
Note that you'll need to grab its value from the UI thread, since it's per-thread.
This question already has answers here:
How do I update the GUI from another thread?
(47 answers)
Closed 9 years ago.
I have a thread that runs in parallel with the Main Form (UI). All it does (for now) is increment a counter every second. I want to display the value of the counter using a label in Windows Forms. Is that possible?
When I try the following code, I get a compile error in the ShowValue method. I have to declare ShowValue "static" so that I can call it from the background thread. But if I do that, I cannot use the "this." to access the label in ShowValue Form1.
Is this the correct way to do this?
Any tip would be appreciated, thanks!
private void count_secs()
{
while (!stopThread)
{
if (stopThread)
{
break;
}
num2++; // increment counter
Form1.ShowValue(num2); // display the counter value in the main Form
try
{
Thread.Sleep(1000); // wait 1 sec.
}
catch (ThreadInterruptedException)
{
if (stopThread)
{
break;
}
}
}
}
Then in my Form1 class, I have:
public static void ShowValue(int num)
{
this.label7.Text = num.ToString();
// compiler error here: "Keyword 'this' is not valid in a static method.
}
You can't refer a local variable (this.label7) in the static method ShowValue(int num)
your method should look like this:
public void ShowValue(int num)
{
if(label7.InvokeREquired)
{
Action a = () => ShowValue(num);
label7.Invoke(a);
}
else
this.label7.Text = num.ToString();
}
in this code, replace the static call to your form with an instance:
private void count_secs()
{
var frm = new Form1(); //create instance
frm.Show(); // show form
while (!stopThread)
{
if (stopThread)
{
break;
}
num2++; // increment counter
//use form instance
frm.ShowValue(num2); // display the counter value in the main Form
try
{
Thread.Sleep(1000); // wait 1 sec.
}
catch (ThreadInterruptedException)
{
if (stopThread)
{
break;
}
}
}
EDIT
You might want to decalre the form instance outside method count_secs()
You cannot randomly access GUI elements from different threads. The short answer to your problem is: Use existing structures.
If you just want to do things frequently, use a Timer. It will notify your main thread (that "owns" the GUI) when the time is up and you can update the GUI element there.
If you really want to create your own thread, use a Backgroundworker. It will offer thread-safe events from which you can update your GUI elements.
Your first issue is to get the Form Instance, if you don't have the Form instance on your calling form then you cause Application.OpenForms property like:
Form1 frm = Application.OpenForms["Form1"] as Form1;
if(frm != null)
frm.ShowValue(num2);
Your second issue is that you need to modify your method as instance method and to save it from Cross threaded exception modify it like:
public void ShowValue(int num)
{
if (label7.InvokeRequired)
{
label7.BeginInvoke((MethodInvoker)delegate { label7.Text = num.ToString(); });
}
else
{
label7.Text = num.ToString();
}
}
Two issues:
You cannot use the this reference from a static context.
You cannot update your UI from a background thread.
Solutions:
Mark the method ShowValue as an instance method (ie. get rid of the static)
Use a background worker or read this question which explains it very well
It's not mandatory to make the ShowValue function static. Leave it as non static and replace your line of logic Form1.ShowValue(num2) with following code.
if (label1.InvokeRequired)
label1.BeginInvoke(new Action(() => ShowValue(num2)));
else
label1.Invoke(new Action(() => ShowValue(num2)));
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
asp.net update UI using multi-thread
I started a thread to process on background.And i want to change my label on page after finished proceess.
But my value doesn't change.
my sample :
protected void Page_Load(object sender, EventArgs e)
{
new Thread(() => ActionStart()) { IsBackground = true }.Start();
}
bool _shouldStop = false;
public void ActionStart()
{
while (!_shouldStop)
{
// ....
requestStop();
}
//thread finished
ChangeValue();
}
private void ChangeValue()
{
lab.Text = "Changed";
}
private void requestStop()
{
_shouldStop = true;
}
I'm afraid running this code on a Thread won't help you because you can't make sure the thread will complete before it's to late considering the page life cycle. Generally, the last chance you got to alter a control's value is the it's PreRender event, but that depends on how it has been developed.
However, if you need to do processing in parallel, you still can use a Parallel.Foreach, for instance, inside a page event like PageLoad to take advantage of parallelism without being asynchronous (Parallel.Foreach will only return when all threads complete).
I need to update my text box continuously after clicking the button but the button should perform its remaining task as it is.
simple is that when click event is performed then Text box should not wait for the completion of click event but to start updating its text continuously.
sample code
using System.threading;
namespace name
{
public class sA
{
public void th()
{
textbox.invoke(new MethodInvoke(()=> textbox.AppendText("hello\n")));
}
private void Button1Click(object sender, EventArgs e)
{
thread cThread=new thread(th);
cThread.start();
while(true)
{
// do any thing
}
}
}
}
Important :: when it performs the event " Cthread.start();" text box should immediately start updating the text while the remaining functions of click event like "while loop" should perform in parallel.
IF this is inside Windows Forms.. then add Application.DoEvents(); anywhere in the loop
e.g.
private void Button1Click(object sender, EventArgs e)
{
thread cThread=new thread(th);
cThread.start();
while(true)
{
// do any thing
textbox.Invalidate();
Application.DoEvents(); // Releases the current thread back to windows form
// NOTE Thread sleep different in Application.DoEvents();
//Application.DoEvents() is available only in System.Windows.Forms
}
}
Hope this help you although late.. :)
Your while(true) block has to happen on another thread as well.
Right now its blocking the UI thread from performing any updates.
Method th() is running on a background thread but the call to Invoke can't run until the UI thread is available again.
If I understood your question correctly, you need to keep updating the TextBox's text while the button click procedure is running inside it's "while" loop. You didn't really specify where will the textbox be updated from, but I will assume that it is coming from the code inside your "while" loop.
As "akatakritos" has stated, your while loop inside the button click is the reason why your application is halting. That happens because the while loop is blocking the User Interface (UI) Thread.
What you should be doing is moving the code inside your "while" loop to run inside a different thread, and use the button click to start this new thread.
Here is a way to do this, maybe not the best, but it will do what you need:
Create a new class:
public class ClassWithYourCode
{
public TextBox TextBoxToUpdate { get; set; }
Action<string> updateTextBoxDelegate;
public ClassWithYourCode()
{ }
public void methodToExecute()
{
bool IsDone = false;
while (!IsDone)
{
// write your code here. When you need to update the
// textbox, call the function:
// updateTextBox("message you want to send");
// Below you can find some example code:
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
updateTextBox(string.Format("Iteration number: {0}", i));
}
// Don't forget to set "IsDone" to "true" so you can exit the while loop!
IsDone = true;
}
updateTextBox("End of method execution!");
}
private void updateTextBox(string MessageToShow)
{
if (TextBoxToUpdate.InvokeRequired)
{
updateTextBoxDelegate = msgToShow => updateTextBox(msgToShow);
TextBoxToUpdate.Invoke(updateTextBoxDelegate, MessageToShow);
}
else
{
TextBoxToUpdate.Text += string.Format("{0}{1}", MessageToShow, Environment.NewLine);
}
}
}
and, inside your button1_Click method, you can add the following code:
private void button1_Click(object sender, EventArgs e)
{
ClassWithYourCode myCode = new ClassWithYourCode();
myCode.TextBoxToUpdate = textBox1;
Thread thread = new Thread(myCode.methodToExecute);
thread.Start();
}
Now, your "while" loop is executing inside a new thread and, whenever you need to update the textbox, you do so from the UI thread, because you cannot update Windows Forms controls from a thread other than the UI thread.