Reputation: 442
I have a function
public void ShowAllFly()
{
cbFly.Items.Clear();
cbFly.Items.Add("Uçuş Seçiniz...");
dsFlyTableAdapters.tblFlyTableAdapter _t=new KTHY.dsFlyTableAdapters.tblFlyTableAdapter();
dsFly _mds = new dsFly();
_mds.EnforceConstraints = false;
dsFly.tblFlyDataTable _m = _mds.tblFly;
_t.Fill(_m);
foreach (DataRow _row in _m.Rows)
{
cbFly.Items.Add(_row["FlyID"].ToString()+"-"+_row["FlyName"].ToString() + "-" + _row["FlyDirection"].ToString() + "-" + _row["FlyDateTime"].ToString());
}
_Thread.Abort();
timer1.Enabled = false;
WaitPanel.Visible = false;
}
In Form_Load Function Like this;
{
_Thread = new System.Threading.Thread(new System.Threading.ThreadStart(ShowAllFly));
_Thread.Start();
_Thread.Priority = System.Threading.ThreadPriority.Normal;
}
But When I run it;
in ShowAllFly function
cbFly.Items.Clear(); ---- HERE Gives ERROR LIKE Control.Invoke must be used to interact with controls created on a separate thread.
What is the problem?
Upvotes: 10
Views: 51819
Reputation: 1164
It`s the best way for work by controls in a thread.
First you have to use from a Single Thread Apartment thread.
...
Thread th = new Thread(yourThreadStart);
th.SetApartmentState(ApartmentState.STA);
th.Start();
...
Next copy this method between your code!
public static void SetControlThreadSafe(Control control, Action<object[]> action, object[] args)
{
if (control.InvokeRequired)
try { control.Invoke(new Action<Control, Action<object[]>, object[]>(SetControlThreadSafe), control, action, args); } catch { }
else action(args);
}
Finally your controls changes must be done like below:
...
SetControlThreadSafe(textbox1, (arg) =>
{
textbox1.Text = "I`m Working in a Thread";
}, null);
...
Enjoy...
Upvotes: 0
Reputation: 1500525
There are two golden rules of threading in Windows Forms:
In order to interact with the UI from a different thread, you need to "marshall" the call to the UI thread, using a delegate and calling Control.Invoke
/BeginInvoke
. You can test whether or not you need to call Invoke
using the InvokeRequired
property, but these days I personally tend to just do it anyway - there's not much penalty for invoking when you don't need to.
Lambda expressions in C# 3 (or anonymous methods in C# 2) make this a lot more pleasant as well.
For instance, you could use:
cbFly.Invoke((MethodInvoker)(() => cbFly.Items.Clear()));
All the brackets get in the way a bit, so you might want to add an extension method like this, if you're using C# 3:
public static void Invoke(this Control control, MethodInvoker action)
{
control.Invoke(action);
}
Then you could do:
cbFly.Invoke(() => cbFly.Items.Clear());
which is a good deal simpler. Usually you can get away with using a MethodInvoker
by capturing any variables you need to access within the delegate.
See my threading tutorial or Joe Albahari's for more details.
As a secondary matter, I see you're using Thread.Abort
- in fact on your own thread, despite it having other calls after it. Why? Aborting any thread other than your own is an "emergencies only" type call (which should usually be followed by the app being unloaded anyway) and I can't see any reason to abort the current thread when there's still work to be done afterwards...
Upvotes: 54
Reputation: 293
It must be invoke ... But invoke have to wait still main thread i mean you not get error this way but this is not exacly working parallel if you want to go more than one process at same time just create more then one thread
Thread thread = new Thread(new delegate_method(method));//you must create delegate before
thread.start ();
Thread thread2 = new Thread(new delegate_method(method2));//you must create delegate before
thread.start ();
handle two process same time
void method ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}
void method2 ()
{
//do something here -- working background Remember can not control any UI control from here
finish_thread()
}
void finish_thread()
{
if(invoke.Required)
{
//Here you have to call delegate method here with UI
BeginInvoke(new delegate_method(finish_thread));
}
else
{
//Now you can control UI thread from here and also you finished background work
//Do something working with UI thread
textBox.Text = "";
}
}
Upvotes: 0
Reputation: 46044
I've always found this article helpful on this particular issue.
In your example, you're trying to modify various controls from a thread that did not create the control. To get around this issue given your example, do this instead (assuming that the ShowAllFly() method is a method on your form):
public void ShowAllFly()
{
Invoke((MethodsInvoker) delegate {
cbFly.Items.Clear();
cbFly.Items.Add("Uçuş Seçiniz...");
dsFlyTableAdapters.tblFlyTableAdapter _t =
new KTHY.dsFlyTableAdapters.tblFlyTableAdapter();
dsFly _mds = new dsFly();
_mds.EnforceConstraints = false;
dsFly.tblFlyDataTable _m = _mds.tblFly;
_t.Fill(_m);
foreach (DataRow _row in _m.Rows)
{
cbFly.Items.Add(_row["FlyID"].ToString() + "-" +
_row["FlyName"].ToString() + "-" +
_row["FlyDirection"].ToString() + "-" +
_row["FlyDateTime"].ToString());
}
//_Thread.Abort(); // WHY ARE YOU TRYING TO DO THIS?
timer1.Enabled = false;
WaitPanel.Visible = false;
} );
}
Just to emphasize the point @Jon Skeet made, I've commented out the call to abort the thread. The thread will end of its own accord. There's no reason to abort it in this fashion.
Upvotes: 4
Reputation: 11734
Interaction on controls in another (ui)thread need to be invoked like so:
public delegate void ProcessResultDelegate(string result); void ProcessResult(string result) { if (textBox1.InvokeRequired) { var d = new ProcessResultDelegate(ProcessResult); d.Invoke(result); } else { textBox1.Text = result; } }
Upvotes: 7