我尝试使用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;
}
}
我非常感谢任何帮助,谢谢。
答案 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.Run
和async/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