Cross-Thread Winforms Control Editing

Cross-thread Winforms control editing

You will need to use Control.Invoke method like this:

textbox1.Invoke((MethodInvoker)(() =>
{
textbox1.Text="some text";
}));

Check this article too: Threading in UIs

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

As per Prerak K's update comment (since deleted):

I guess I have not presented the question properly.

Situation is this: I want to load data into a global variable based on the value of a control. I don't want to change the value of a control from the child thread. I'm not going to do it ever from a child thread.

So only accessing the value so that corresponding data can be fetched from the database.

The solution you want then should look like:

UserContrl1_LOadDataMethod()
{
string name = "";
if(textbox1.InvokeRequired)
{
textbox1.Invoke(new MethodInvoker(delegate { name = textbox1.text; }));
}
if(name == "MyName")
{
// do whatever
}
}

Do your serious processing in the separate thread before you attempt to switch back to the control's thread. For example:

UserContrl1_LOadDataMethod()
{
if(textbox1.text=="MyName") //<<======Now it wont give exception**
{
//Load data correspondin to "MyName"
//Populate a globale variable List<string> which will be
//bound to grid at some later stage
if(InvokeRequired)
{
// after we've done all the processing,
this.Invoke(new MethodInvoker(delegate {
// load the control with the appropriate data
}));
return;
}
}
}

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on

The data received in your serialPort1_DataReceived method is coming from another thread context than the UI thread, and that's the reason you see this error.

To remedy this, you will have to use a dispatcher as descibed in the MSDN article:

How to: Make Thread-Safe Calls to Windows Forms Controls

So instead of setting the text property directly in the serialport1_DataReceived method, use this pattern:

delegate void SetTextCallback(string text);

private void SetText(string text)
{
// 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.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}

So in your case:

private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
txt += serialPort1.ReadExisting().ToString();
SetText(txt.ToString());
}

How to fix issue cross thread when I use add controls in asynchronous? winform c#

if (fileCanvas.InvokeRequired)
{
fileCanvas.Invoke((MethodInvoker)delegate {
fileCanvas.Controls.Add(canvas);
});
}
else
{
fileCanvas.Controls.Add(canvas);
}

This is easiest way to do it i guess.

Cross thread operation while working event raised by other object

UI controls can only be updated from UI thread.
If you are working with WinForms you should do:

mycontrol.Invoke((MethodInvoker)(() =>
{
mycontrol.Text="some text";
}));

And if you are in WPF then you should use the dispatcher:

myControl.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => myControl.Text = ""));

How to access a WinForms control from another thread i.e. synchronize with the GUI thread?

You can use this:

new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;

TcpListener server = null;

while (true)
{
...
this.SynUI(()=>
{
if ( checkbox.Checked )
{
}
});
...
}
}).Start();

Or:

...
bool checked = false;
this.SynUI(()=> { checked = checkbox.Checked; });
...

Having:

static public class SyncUIHelper
{

static public Thread MainThread { get; private set; }

// Must be called from Program.Main
static public void Initialize()
{
MainThread = Thread.CurrentThread;
}

static public void SyncUI(this Control control, Action action, bool wait = true)
{
if ( control == null ) throw new ArgumentNullException(nameof(control));
if ( !Thread.CurrentThread.IsAlive ) throw new ThreadStateException();
Exception exception = null;
Semaphore semaphore = null;
Action processAction = () =>
{
try
{
action();
}
catch ( Exception ex )
{
exception = ex;
}
};
Action processActionWait = () =>
{
processAction();
semaphore?.Release();
};
if ( control.InvokeRequired && Thread.CurrentThread != MainThread )
{
if ( wait ) semaphore = new Semaphore(0, 1);
control.BeginInvoke(wait ? processActionWait : processAction);
semaphore?.WaitOne();
}
else
processAction();
if ( exception != null )
throw exception;
}

}

Adding in the Program.Main before the Application.Run:

SyncUIHelper.Initialize();

You can find on stack overflow various ways to synchronize threads with the UI thread like:

How do I update the GUI from another thread?

Update UI from Class (multithreaded)?

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on

There is BackgroundWorker too.

Update WinForm Controls from another thread _and_ class

It does not matter from which class you are updating the form. WinForm controls have to be updated on the same thread that they were created on.

Hence, Control.Invoke, allows you to execute a method on the control on its own thread. This is also called asynchronous execution, since the call is actually queued up and executed separately.

Look at this article from msdn, the example is similar to your example. A separate class on a separate thread updates a list box on the Form.

----- Update
Here you do not have to pass this as a parameter.

In your Winform class, have a public delegate that can update the controls.

class WinForm : Form
{
public delegate void updateTextBoxDelegate(String textBoxString); // delegate type
public updateTextBoxDelegate updateTextBox; // delegate object

void updateTextBox1(string str ) { textBox1.Text = str1; } // this method is invoked

public WinForm()
{
...
updateTextBox = new updateTextBoxDelegate( updateTextBox1 ); // initialize delegate object
...
Server serv = new Server();

}

From the ClientConnection Object, you do have to get a reference to the WinForm:Form object.

class ClientConnection
{
...
void display( string strItem ) // can be called in a different thread from clientConnection object
{
Form1.Invoke( Form1.updateTextBox, strItem ); // updates textbox1 on winForm
}
}

In the above case, 'this' is not passed.

Why can you cross thread adding controls in WinForms, but not WPF?

WinForms isn't as strict about checking for cross-threading issues. This is probably because WinForms doesn't actually have controls such as labels. Rather, they are just wrappers around the real controls that are implemented at the OS level.

Since this is an implementation detail, there is no guarantee that your WinForms code will continue to work in the future. (That said, it isn't under active development so it will probably continue working.)



Related Topics



Leave a reply



Submit