views:

463

answers:

3

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 funtion

cbFly.Items.Clear(); ----  HERE Gives ERROR  LIKE  Control.Invoke must be used to interact with controls created on a separate thread.

PLEAS HELP ME! WHAT IS THE PROBLEM?

+4  A: 

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;
    }
}
rdkleine
Good solution; byt I would recommend using the `Action<T>` from the framework rather than rolling (and maintaining) your own.
Fredrik Mörk
Yep.. this is pretty much the standard way to do this. The reason this is needed is that textBox1.Text can only be changed on the thread in which the textbox was created - invoking is the process used to return to that thread.
Charlie Salts
@Fredrik: You're right, but this example is seen more often, in large part because it's been around longer. Either method will work.
Charlie Salts
@Fredrik: Could you work out an example of using Action<T>?
rdkleine
+5  A: 

There are two golden rules of threading in Windows Forms:

  • Don't touch any control properties or methods (other than those explicitly listed as being okay) from any thread other than the one which created the control's "handle" (usually there's just one UI thread)
  • Don't block the UI thread for any significant length of time, or you'll make the application unresponsive

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 no 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...

Jon Skeet
very nice solution jon. thank you. I Use this solution now.
atromgame
Then why not accept the answer!?
badbod99
A: 

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.

Matt Davis
Your are The Best Matt. Thank you. It it works very nice.
atromgame