Ваша основная проблема заключается в том, что события BackgroundWorker
выполняются в потоке рабочего, а не в потоке пользовательского интерфейса.Но доступ к элементам пользовательского интерфейса возможен только из потока пользовательского интерфейса.
Для его решения я предлагаю использовать 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;
}