on button_click事件,我有一个需要很长时间的查询。所以我在背景工作者上运行它
private void btnGenerate_Click(object sender, EventArgs e)
{
btnGenerate.Enabled = false;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += delegate (object s, DoWorkEventArgs args)
{
Data = DataLoader.GetData(Environment.UserName); // stored procedure execution
if (Data != null)
{
GenerateExcel(Data);
GenerateSingleExcel(Data);
}
};
worker.RunWorkerCompleted += delegate (object s, RunWorkerCompletedEventArgs args)
{
progressBar1.Visible = false;// ProgressBarStyle.Marquee
btnGenerate.Enabled = true;
};
worker.RunWorkerAsync();
}
我的问题是,我需要设置
btngenerate.enable = false;
在button_click处。并在执行后启用。
我在RunWorkerCompleted
中尝试了它,但它显示
'跨线程操作无效:从其创建的线程以外的线程访问的" btngenerate"。
任何建议都会有所帮助。
您的主要问题是BackgroundWorker
的事件是在工作人员的线程上执行的,而不是UI线程。但是只能从UI线程访问UI元素。
要解决它,我建议使用async/await
而不是BackgroundWorker
:
// declare as async
private async void btnGenerate_Click(object sender, EventArgs e)
{
btnGenerate.Enabled = false;
Data = await Task.Run(() => {
var data = DataLoader.GetData(Environment.UserName); // stored procedure execution
if (data != null)
{
GenerateExcel(Data);
GenerateSingleExcel(Data);
}
return data; // as suggested by Vlad, don't set Data on this thread
});
// this is now executed back on the UI thread
progressBar1.Visible = false;// ProgressBarStyle.Marquee
btnGenerate.Enabled = true;
}
如果DataLoader
提供异步的GetDataAsync
,甚至不需要Task.Run()
。
如果无法(无论出于何种原因)async
,则您的RunWorkerCompleted
处理程序应使用Invoke
或BeginInvoke
:
worker.RunWorkerCompleted += OnRunWorkerCompleted;
//...
public void OnRunWorkerCompleted(object s, RunWorkerCompletedEventArgs args)
{
if (InvokeRequired)
{
// not on the UI thread - use (Begin-)Invoke
BeginInvoke(new RunWorkerCompletedEventHandler(OnRunWorkerCompleted), s, args);
return;
}
// now we're on the UI thread
progressBar1.Visible = false;// ProgressBarStyle.Marquee
btnGenerate.Enabled = true;
}