Reputation: 1779
"Cross-thread operation is not valid" exception
I experienced this exception many times, but all those times I was setting the value of a control. That time I solved using a function called SetControlPropertyThreadSafe()
, which was suggested by someone on stackoverflow.com only. But this time I am getting this exception when I am trying to get the value of comboBox. Here is the code:
string cat;
private void button1_Click(object sender, EventArgs e)
{
if (textBox1.Text.Trim().Length > 20)
{
System.Threading.Thread t = new System.Threading.Thread(addQuickTopic);
t.Start();
}
else
MessageBox.Show("The length of the title must be greater than 20", "Title length invalid", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
public string tTitle="";
void addQuickTopic()
{
Global.SetControlPropertyThreadSafe(button1, "Text", "Working...");
Global.SetControlPropertyThreadSafe(button1, "Enabled", false);
Topic t = new Topic();
t.setTitle(textBox1.Text);
t.setDescription(" ");
t.setDesID(Global.getMd5Hash(Common.uid+DateTime.Today.ToString()+DateTime.Today.Millisecond.ToString()));
t.setUsrID(Common.uid);
t.setReplyToID("");
t.setConDate("0");
cat = CategoryList.SelectedValue.ToString();
As you can see I am getting the textBox1.Text directly without applying any thread safe operation. But at the last line when trying to fetch comboBox's selected value, I am getting this exception. So can any one please suggest me what to do in this situation? Following is the code for my thread safe function for setting control's value:
public static void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue)
{
try
{
if (control.InvokeRequired)
{
control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue });
}
else
{
control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue });
}
}
catch (Exception)
{ }
}
Should I need to create a similar get
function? Or any other solution available?
Upvotes: 3
Views: 1988
Reputation: 564471
Right now, you're getting the value from the TextBox
, via: t.setTitle(textBox1.Text);
. This will also fail.
Should I need to create a similar get function? Or any other solution available?
Yes, you need an option for get. I would recommend not using reflection and text, however, and rework this to use generic methods and lambdas.
public static void SetControlPropertyThreadSafe<T>(T control, Action<T> action) where T : Control
{
if (control.InvokeRequired)
{
control.Invoke(action);
}
else
{
action();
}
}
This would allow you to write this strongly typed, ie:
Global.SetControlPropertyThreadSafe(button1, b => b.Text = "Working...");
You can also make a strongly typed get method:
public static U GetControlPropertyThreadSafe<T,U>(T control, Func<T,U> func) where T : Control
{
if (control.InvokeRequired)
{
return (U)control.Invoke(func, new object[] {control});
}
else
{
return func(control);
}
}
Which then lets you write:
t.setTitle(Global.GetControlPropertyThreadSafe(textBox1, t => t.Text));
You could also use the same methods for getting and setting the combo box items.
Upvotes: 4
Reputation: 3230
You need a similar get function, which calls Invoke if required. This depends from the threading model of the GUI, which states that all the methods that interact with the GUI should only be called by the GUI thread.
It's not important if you're trying to read, to write, or do anything else. If you're not on the GUI thread, you must use Invoke.
Upvotes: 2