Всем добрый день! В программе потребовалось реализовать отправку запроса в БД и получение ответа в отдельном потоке, решил обратиться к backgroundworker. При открытии второй формы в программе и клике на чекбоксе нужно запустить третью форму (с прогрессбаром и таймером) и в воркере получить таблицу из базы, после получения отобразить в датагриде на второй форме и закрыть третью. Написал следующий код, но работает странно - датагрид не обновляется и третья форма не закрывается автоматически. В чем косяк? Также планировал добавить возможность прервать работу воркера из 3 формы по клику на кнопку "Прервать", но пока остановился на этом, через Threads сделать получилось, но хочется более верно реализовать (закомментированные куски кода остались от другой идеи)
public partial class Organizations : Form
{
Progress ThirdForm;
public Thread potok;
public Thread potok1;
public BackgroundWorker BW;
public Organizations()
{
InitializeComponent();
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(
backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged +=
new ProgressChangedEventHandler(
backgroundWorker1_ProgressChanged);
backgroundWorker1.WorkerSupportsCancellation = true;
}
public delegate void Del();
DataTable Loading(BackgroundWorker worker, DoWorkEventArgs e)
{
bool b2 = false;
DataTable dt;
if (worker.CancellationPending)
{
e.Cancel = true;
}
else
{
// Получение строки соединения с базой данных и заполнение таблицы на CalculationForm
var cn = new SqlConnection();
SqlDataReader myReader = null;
cn.ConnectionString = ConnString.GetConnectionString();
cn.Open();
// Идентификатор расчета
if (((Reports)(Application.OpenForms[0])).textBox1.Text == "")
{
MessageBox.Show("Необходимо сначала выбрать расчет!", "Обработчик кнопки Организации", MessageBoxButtons.OK, MessageBoxIcon.Error);
return dt = null;
}
else
{
int IdCostsResultTask = Convert.ToInt32(((Reports)(Application.OpenForms[0])).textBox1.Text);
string sqlQuery = @" SELECT cr.ExportIdListEdu 'ExportIdListEdu', ReestrEdu.IdListEdu 'IdListEdu',slovar..ListEdu.Name 'ShortName', slovar..ListEdu.LongName 'Name' FROM ReestrEdu " +
" LEFT JOIN slovar..ListEdu on ReestrEdu.IdListEdu = slovar..ListEdu.IdListEdu " +
" LEFT JOIN CostsResult as cr on ReestrEdu.IdListEdu = cr.ExportIdListEdu or ReestrEdu.IdListEdu = cr.IdListEdu" +
" WHERE cr.IdCostsResultTask = " + IdCostsResultTask + " and slovar..ListEdu.LongName is not null " +
" Group By cr.ExportIdListEdu, ReestrEdu.IdListEdu, slovar..ListEdu.Name, slovar..ListEdu.LongName ORDER BY 1,2 ";
var myCommand = new SqlCommand(sqlQuery, cn);
myCommand.CommandTimeout = 600;
myReader = myCommand.ExecuteReader();
using (myReader)
{
var table = new System.Data.DataTable();
table.Load(myReader);
dt = table;
}
myReader.Dispose();
cn.Close();
b2 = true;
}
}
return dt;
}
DataTable Loading2(BackgroundWorker worker, DoWorkEventArgs e)
{
bool b2 = false;
DataTable dt2;
if (worker.CancellationPending)
{
e.Cancel = true;
}
else
{
// Получение строки соединения с базой данных и заполнение таблицы на CalculationForm
var cn = new SqlConnection();
SqlDataReader myReader = null;
cn.ConnectionString = ConnString.GetConnectionString();
cn.Open();
// Идентификатор расчета
if (((Reports)(Application.OpenForms[0])).textBox1.Text == "")
{
MessageBox.Show("Необходимо сначала выбрать расчет!", "Обработчик кнопки Организации", MessageBoxButtons.OK, MessageBoxIcon.Error);
return dt2 = null;
}
else
{
int IdCostsResultTask = Convert.ToInt32(((Reports)(Application.OpenForms[0])).textBox1.Text);
string sqlQuery = @" SELECT cr.ExportIdListEdu 'ExportIdListEdu', ReestrEdu.IdListEdu 'IdListEdu',slovar..ListEdu.Name 'ShortName', slovar..ListEdu.LongName 'Name' FROM ReestrEdu " +
" LEFT JOIN slovar..ListEdu on ReestrEdu.IdListEdu = slovar..ListEdu.IdListEdu " +
" LEFT JOIN CostsResult as cr on ReestrEdu.IdListEdu = cr.ExportIdListEdu" +
" WHERE cr.IdCostsResultTask = " + IdCostsResultTask + " and slovar..ListEdu.LongName is not null " +
" Group By cr.ExportIdListEdu, ReestrEdu.IdListEdu, slovar..ListEdu.Name, slovar..ListEdu.LongName ORDER BY 1 ";
var myCommand = new SqlCommand(sqlQuery, cn);
myCommand.CommandTimeout = 600;
myReader = myCommand.ExecuteReader();
using (myReader)
{
var table = new System.Data.DataTable();
table.Load(myReader);
dt2 = table;
}
myReader.Dispose();
cn.Close();
b2 = true;
}
}
return dt2;
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
}
}
private void OrgDatagrid_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
((Reports)(Application.OpenForms[0])).listBox1.Items.Insert(0, "1");
this.Close();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
if (checkBox1.Checked)
{
ThirdForm = new Progress();
ThirdForm.ShowDialog();
e.Result = Loading(worker, e);
}
else
{
ThirdForm = new Progress();
ThirdForm.ShowDialog();
e.Result = Loading2(worker, e);
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else
{
ThirdForm.Close();
OrgDatagrid.DataSource = e.Result;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
}
Код 3 формы (с таймером, прогрессбаром и кнопкой отмены асинхронной операции):
public partial class Progress : Form
{
public Progress()
{
InitializeComponent();
// Обрабатываем событие таймера.
timer1.Interval = 1000;
timer1.Start();
}
public void timer1_Tick(object sender, EventArgs e)
{
TimeLabel.Text = DateTimer.ToString("mm:ss");
DateTimer = DateTimer.AddSeconds(1);
}
private void BtnCancel_Click(object sender, EventArgs e)
{
Organizations f = new Organizations();
if (f.backgroundWorker1.WorkerSupportsCancellation == true)
{
// Cancel the asynchronous operation.
f.backgroundWorker1.CancelAsync();
}
}
UPD: Решил проблему, перенеся вызов ThirdForm в метод checkBox1_CheckedChanged после вызова воркера. Беда была именно в вызове 3 формы из backgroundWorker1_DoWork. Внутри методов worker эти обращения работают некорректно (по крайней мере, не так, как хотелось бы). Однако осталась другая проблема - кнопка отмены операции в воркере с 3 формы не срабатывает (просто ничего не происходит). Пока не понимаю, как корректно вызвать завершение worker из другой формы.
UPD2: Проблема с кнопкой решена следующим образом:
Изменения в коде на 3 форме:
public event EventHandler Cancel = delegate { };
private void BtnCancel_Click(object sender, EventArgs e)
{
Cancel(sender, e);
timer1.Stop();
}
Обработка во второй форме:
void CancelProcess(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
if (checkflag)
{
checkBox1.Checked = false;
ThirdForm.Close();
ThirdForm.timer1.Stop();
ThirdForm.TimeLabel.Text = "";
ThirdForm.DateTimer = new DateTime(0, 0);
}
else
{
checkBox1.Checked = true;
//backgroundWorker1.Dispose();
ThirdForm.Close();
ThirdForm.timer1.Stop();
ThirdForm.TimeLabel.Text = "";
ThirdForm.DateTimer = new DateTime(0, 0);
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy != true)
{
// Start the asynchronous operation.
checkflag = checkBox1.Checked;
ThirdForm = new Progress();
ThirdForm.timer1.Stop();
ThirdForm.TimeLabel.Text = "";
ThirdForm.Cancel += CancelProcess;
backgroundWorker1.RunWorkerAsync();
ThirdForm.timer1.Start();
ThirdForm.ShowDialog();
}
}
Форма должна закрыться или вывестись сообщение об ошибке (либо MessageBox, либо программа упадет). Ошибка может не появиться, если возникла не в основном потоке (например, если backgroundWorker1_RunWorkerCompleted сработал в другом потоке).
предлагаю на всякий случай вставить Invoke внутрь этого метода
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Invoke(new Action(() =>
if (e.Error != null)
{
MessageBox.Show(e.Error.Message);
}
else
{
ThirdForm.Close();
OrgDatagrid.DataSource = e.Result;
MessageBox.Show("Работа завершена");
}
));
}
Заодно добавил лишний MessageBox, чтобы убедиться, что backgroundWorker1_RunWorkerCompleted вобще срабатывает.
Что касается прерывания BackgroundWorker с помощью метода Abort(), как для Thread, то, к сожалению, он его не поддерживает. Конечно же делать Abort() нежелательно, т.к. остается мертвый Connection. Хотя сборщик мусора его потом закроет.
Думаю есть смысл полностью отказаться от BackgroundWorker и все реализовать на Thread, помещая весь код работы с интерфейсом в Invoke() (формы нельзя менять в другом потоке).
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники
Продвижение своими сайтами как стратегия роста и независимости