1

I am new to threading and I am getting this error

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

I dont know if I created this thread correctly but every time its given me that error. Does anyone know how to solve this issue. I already tried:

this.Invoke(new MethodInvoker(delegate()
{
     da.SelectCommand.Parameters.AddWithValue("@QueueID", comboBoxLocation.SelectedValue.ToString());
}));

And I am still getting that error.

string branch;
SpeechSynthesizer reader = new SpeechSynthesizer();
Thread t;
bool flag;

//SqlDataReader dr2;
public Main()
{
    InitializeComponent();
}

private void btnStart_Click(object sender, EventArgs e)
{
    lblStatus.Text = "Started!";
    btnStop.Enabled = true;
    btnStart.Enabled = false;
    t = new Thread(Begin); //calls Begin method
    t.Name = "Queue"; //thread name
    t.Start(); //Starts thread
    flag = false;
    branch = comboBoxLocation.SelectedValue.ToString();
}

private void btnStop_Click(object sender, EventArgs e)
{
    lblStatus.Text = "Voice Application Termintated!";
    btnStart.Enabled = true;
    btnStop.Enabled = false;
    t.Abort(); //Kills Thread
    flag = true;
}     

//Began method
public void Begin()
{
    double announce;
    double announceID;

    string Stringannounce;
    string ticketNum, station, id, displaynum;
    string speak;

    try
    {
        using (SqlConnection connStr = new SqlConnection(ConfigurationManager.ConnectionStrings["AnnounceConnString"].ConnectionString))
        {
            while (true)
            {
               //Selects Last record written to tblAnnounce 
                SqlCommand sqlcommandStart = new SqlCommand("SELECT id, AnnounceID, AddDate FROM tblAnnounce ORDER by AddDate ASC", connStr);
                connStr.Open();

                SqlDataReader dr = sqlcommandStart.ExecuteReader();

                if (dr.HasRows)
                {
                    while (dr.Read())
                    {
                        Stringannounce = dr["AnnounceID"].ToString();
                        announceID = Convert.ToDouble(Stringannounce);


                        SqlDataAdapter da = new SqlDataAdapter("SELECT TOP 1 id, qdate, ticket_number, QueueID, received, displaynum, station, transcodevoiced FROM vwAnnounce WHERE QueueID = @QueueID ORDER by received ASC", connStr);
                        //da.SelectCommand.CommandType = CommandType.StoredProcedure;

                        DataSet ds = new DataSet();
  *****************da.SelectCommand.Parameters.AddWithValue("@QueueID", comboBoxLocation.SelectedValue.ToString());***** //ERROR HERE
                        da.Fill(ds);

                        DataTable dt = new DataTable();
                        dt = ds.Tables[0];


                        foreach (DataRow dr2 in dt.Rows)
                        {
                            id = dr2["id"].ToString();
                            announce = Convert.ToDouble(id);
                            ticketNum = dr2["ticket_number"].ToString();
                            station = dr2["station"].ToString();
                            //transcodevoiced = dr2["transcodevoiced"].ToString();
                            displaynum = dr2["displaynum"].ToString();
                            SplitString splitter = new SplitString(displaynum);
                            splitter.Split();

                            if (announceID == announce)
                            {
                                // Set a value for the speaking rate.
                                reader.Rate = -4;
                                // Set the volume of the SpeechSynthesizer's ouput.
                                reader.Volume = 85;
                                //String that will be spoken
                                speak = "Now, " + displaynum + ", at #" + station;

                                //The problem with the Speak() function is that it is not threaded, so we use SpeakAsync(). It means that you cannot perform any other function in your windows form until the "reader" object has completed the speech.
                                reader.SpeakAsync(speak);
                                //Pauses system for 1 seconds after voice completion
                                Thread.Sleep(3000);

                                //Deletes record from Database table tblAnnounce
                                SqlCommand delete = new SqlCommand("DELETE FROM tblAnnounce WHERE AnnounceID = @announceID ", connStr);
                                delete.Parameters.AddWithValue("@announceID", announceID);
                                delete.ExecuteNonQuery();
                                reader.Resume();
                            }
                        }                                            
                    }
                    connStr.Close();
                }                       
                dr.Close();
            }
        }
    }
    catch(Exception ex)
    {
        string exception = ex.Message;
    }
}
Furkan Ekinci
  • 2,472
  • 3
  • 29
  • 39
Apollo
  • 1,990
  • 12
  • 44
  • 65
  • How is `comboBoxLocation` being created, is it being created in the designer or is it added dynamicly during runtime? – Scott Chamberlain May 31 '13 at 16:44
  • This is a good resource if you are interested in the explanations. How To: Make Thread-Safe Calls to Windows Forms Controls http://msdn.microsoft.com/en-us/library/ms171728.aspx – p e p May 31 '13 at 16:51
  • Sidenote: Do *not* use `Thread.Abort()` to kill a thread. Use some form of synchronisation to tell it to stop. [See this documentation](http://msdn.microsoft.com/en-us/library/dd537607.aspx) for an example. – Matthew Watson May 31 '13 at 17:09
  • Combobox is in Form and is dataBound – Apollo May 31 '13 at 18:29

2 Answers2

1

To sort out your problem, the Invoke should put at line below:

    comboBoxLocation.Invoke(
new Action(() => branch = comboBoxLocation.SelectedValue.ToString()); 

Hope this help.

Toan Vo
  • 1,270
  • 9
  • 19
0

You are invoking via the form instead of the control.

Use this:

comboBoxLocation.Invoke(new MethodInvoker(delegate()
{
     da.SelectCommand.Parameters.AddWithValue("@QueueID", comboBoxLocation.SelectedValue.ToString());
}));

to invoke via the control instead.

Here is a great resource on thread-safe calls from Microsoft.

http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx

To improve upon your code, you could also check to see if the invoke is required or not before you do so. Example:

if (comboBoxLocation.InvokeRequired)
{
     comboBoxLocation.Invoke(
          (MethodInvoker)
               delegate {
                    da.SelectCommand.Parameters.AddWithValue("@QueueID", comboBoxLocation.SelectedValue.ToString());
});
else
{
     da.SelectCommand.Parameters.AddWithValue("@QueueID", comboBoxLocation.SelectedValue.ToString());
}
Chris
  • 131
  • 3