无效的线程交叉进程

时间:2015-06-10 07:37:36

标签: c# winforms

我尝试使用datagridview使用带有两个参数ID的来自sql server database的记录触发store procedure。我有两个组合框,它带有参数和一个按钮。在获取数据时,我希望看到进度,因此我为其分配了progressBar但是我收到错误:无效的线程交叉进程:由不同的线程执行对控件ComboBox2的访问而不是创建它的线程。 我的c#代码:

 private void button2_Click(object sender, EventArgs e)
 {
     ProgressBar1.Visible = true;
     ProgressBar1.Style = ProgressBarStyle.Marquee;
     System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(loadTable));
     thread.Start(); 
 }

 private void loadTable()
 {
     // Load Table...        

     string C = ConfigurationManager..["DB"].ConnectionString;
     SqlConnection con = new SqlConnection(C);
     SqlCommand cmd = new SqlCommand();
     cmd.Connection = con;
     cmd.CommandText = ("[dbo].[spInfo]");
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.AddWithValue("@Periode2", comboBox2.SelectedValue.ToString());
     cmd.Parameters.AddWithValue("@Periode1", comboBox3.SelectedValue.ToString());

     try
     {
         // Open connection
         con.Open();

         SqlDataAdapter adapter = new SqlDataAdapter(cmd);

         // Save the results in the DT. Call the adapter statement and fill it in the DT
         DataTable dt = new DataTable();
         adapter.Fill(dt);

         setDataSource(dt);

     }
     catch (Exception ex)
     {
         MessageBox.Show(ex.Message);
     }
     finally
     {
         con.Close();
     }
 }

 internal delegate void SetDataSourceDelegate(DataTable dt);

 private void setDataSource(DataTable dt)
 {
     // Invoke method if required:
     if (this.InvokeRequired)
     {
         this.Invoke(new SetDataSourceDelegate(setDataSource), dt);
     }
     else
     {
         datagridview1.DataSource = dt;
         ProgressBar1.Visible = false;
     }
 }

我非常感谢任何帮助,谢谢。

3 个答案:

答案 0 :(得分:1)

您无法在loadTable()函数中访问comboBox2和comboBox3,因为它在不同的线程中运行。 (这与datagridView1和ProgressBar1的问题相同,你使用代码片段底部的代理解决了这个问题) 如果您只想访问这两个元素,请尝试从_Click()方法将SelectedValues传递给loadTable()函数,而不是在loadTable()函数中读取它(虽然这是一个快速而又脏的解决方案):

在button_Click函数中使用参数化线程启动:

System.Threading.Thread thread = new System.Threading.Thread(new ParameterizedThreadStart(this.loadTable));
thread.Start(new Tuple<string, string>(comboBox2.SelectedValue.ToString(), comboBox3.SelectedValue.ToString())); 

我选择Tuple作为两个组合框值的快速和脏对象类型,但您可以定义一个结构或其他东西。

然后在你的loadTable()函数中:

private void loadTable(object parameters) {
var comboBoxValues = parameters as Tuple<string, string>;
...
cmd.Parameters.AddWithValue("@Periode2",comboBoxValues.Item1);
cmd.Parameters.AddWithValue("@Periode1",comboBoxValues.Item2);
...
}

关键区别在于,在启动新线程之前访问组合框值,并将其值传递给线程,而不是访问线程内的组合框。

答案 1 :(得分:0)

您无法从其他线程修改UI控件。如果要在后台加载数据表,可以使用Task.Runasync/await在后​​台任务中加载数据,然后继续处理UI线程。您根本不需要创建原始线程或使用Invoke

只需确保将组合选择作为值传递给后台任务:

private async void button2_Click(object sender, EventArgs e)
{
    ProgressBar1.Visible = true;
    ProgressBar1.Style = ProgressBarStyle.Marquee;
    var period1=comboBox3.SelectedValue.ToString();
    var period2=comboBox2.SelectedValue.ToString();

    //This will run in the background
    var table=await Task.Run(()=>LoadTable(period1,period2));

    //We're back in the UI
    datagridview1.DataSource = table;
    ProgressBar1.Visible = false;    
}

private async Task<DataTalbe> LoadTable(string period1, string period2)
{
     string C = ConfigurationManager.["DB"].ConnectionString;
     using(var con = new SqlConnection(C))
     using(var cmd = new SqlCommand())
     {
         cmd.Connection = con;
         cmd.CommandText = ("[dbo].[spInfo]");
         cmd.CommandType = CommandType.StoredProcedure;
         cmd.Parameters.AddWithValue("@Periode2",period2);
         cmd.Parameters.AddWithValue("@Periode1", period1);

         con.Open();

         SqlDataAdapter adapter = new SqlDataAdapter(cmd);

         DataTable dt = new DataTable();
         adapter.Fill(dt);

         return dt;
    }
}

答案 2 :(得分:-3)

您可以尝试使用

this.Invoke(()=&gt; setDataSource(dt););

这应该有用......

LG