Form2 с прогрессбаром в отдельном потоке по нажатию на кнопку
Как сделать в отдельном потоке показ Form2 пока грузятся данные с Form1?
Вот код чтения и загрузки данных по нажатию из Form1:
{
string path = "file.xml";
DataSet ds = new DataSet();
ds.ReadXml(path);
dataGridView1.DataSource = ds;
dataGridView1.DataMember = "table1";
MessageBox.Show("Данные успешно загружены", "Готово");
}
frm.Show();
Вот как организовать все эти действия в отдельных потоках я не знаю...точнее голова уже кругом.Столько способов в инете.
Может кто подобное делал?Заранее спасибо.
private delegate void UpdateDataGridViewCallback(DataSet dataSet); // Делегат для метода заполнения DataGridView.
private delegate void CloseProgressCallback(); // Делегат для закрытия диалога прогресс-бара.
public delegate void OnWorkIsDoneHandler(object sender, EventArgs e); // Делегат для события окна.
public event OnWorkIsDoneHandler OnWorkIsDone; // Событие окна: завершение потока чтения.
public Form1()
{
InitializeComponent();
// Подписка на событие.
OnWorkIsDone += Form1_OnWorkIsDone;
}
// Обработчик события окончания работы потока чтения.
private void Form1_OnWorkIsDone(object sender, EventArgs e)
{
waitForm.Invoke(new CloseProgressCallback(CloseProgress));
MessageBox.Show("Загрузка завершена");
}
private void Form1_Load(object sender, EventArgs e)
{
// Инициализация и показ формы с прогресс-баром.
waitForm = new ProgressDlg();
waitForm.Show();
// Создание и запуск рабочего потока.
System.Threading.Thread readThread = new System.Threading.Thread(new System.Threading.ThreadStart(ReadThread));
readThread.Start();
}
private void ReadThread()
{
DataSet ds = new DataSet();
ds.Tables.Add(new DataTable());
System.Threading.Thread.Sleep(5000);
dataGridView1.Invoke(new UpdateDataGridViewCallback(UpdateDataGridView), new object[] { ds });
// Оповещаем основное окно о завершении работы.
if (OnWorkIsDone != null)
{
OnWorkIsDone(this, EventArgs.Empty);
}
}
// Заполнение DataGridView
private void UpdateDataGridView(DataSet dataSet)
{
dataGridView1.DataSource = dataSet;
dataGridView1.DataMember = "table1";
}
// Закрытие диалога прогресс-бара.
private void CloseProgress()
{
if (waitForm != null)
{
waitForm.Close();
}
}
Сделал так,но выдает ошибку:
{
string path = "test.xml";
DataSet ds = new DataSet();
ds.ReadXml(path);
dataGridView1.DataSource = ds;
dataGridView1.DataMember = "table1";
MessageBox.Show("Данные успешно загружены", "Готово");
}
private void ThreadForm() // Создал поток для показа Form2 (Типа подождите идет загрузка)
{
Form2 frm = new Form2();
frm.Show();
}
private void открытьToolStripMenuItem_Click(object sender, EventArgs e) // Запуск потоков
{
Thread t1 = new Thread(new ThreadStart(this.ThreadRead));
t1.Start();
Thread t2 = new Thread(new ThreadStart(this.ThreadForm));
t2.Start();
}
Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll
Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'dataGridView1' не из того потока, в котором он был создан.
Как решить проблему?
Сообщение ошибки:
Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll
Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'dataGridView1' не из того потока, в котором он был создан.
Как решить проблему?
Проблема решается использованием делегата для метода, который должен обновлять состояние dataGridView1, и вызовом этого делегата через метод Invoke():
private void UpdateDataGridView(DataSet dataSet)
{
dataGridView1.DataSource = dataSet;
dataGridView1.DataMember = "table1";
}
private void ThreadRead() // Создал поток для чтения xml и выгрузке в datagridview
{
string path = "test.xml";
DataSet ds = new DataSet();
ds.ReadXml(path);
dataGridView1.Invoke(new UpdateDataGridViewCallback(UpdateDataGridView), new object[] { ds });
MessageBox.Show("Данные успешно загружены", "Готово");
}
Сообщение ошибки:
Необработанное исключение типа "System.InvalidOperationException" в System.Windows.Forms.dll
Дополнительные сведения: Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'dataGridView1' не из того потока, в котором он был создан.
Как решить проблему?
Проблема решается использованием делегата для метода, который должен обновлять состояние dataGridView1, и вызовом этого делегата через метод Invoke():
private void UpdateDataGridView(DataSet dataSet)
{
dataGridView1.DataSource = dataSet;
dataGridView1.DataMember = "table1";
}
private void ThreadRead() // Создал поток для чтения xml и выгрузке в datagridview
{
string path = "test.xml";
DataSet ds = new DataSet();
ds.ReadXml(path);
dataGridView1.Invoke(new UpdateDataGridViewCallback(UpdateDataGridView), new object[] { ds });
MessageBox.Show("Данные успешно загружены", "Готово");
}
Спасибо...все работает.Но тут осталась нерешенной маленькая деталь.Form2 которую я вызываю при выгрузке данных в datagridview,быстро появляется и исчезает,а потом идет заполнение таблицы.Как будто таблица и прогрессбар (он же Form2) работают не одновременно,а отдельно.
Как сделать показ Form2 пока грузится таблица,а как только таблица полностью загрузилась,окно закрывается?
Пробовал использовать не
Сейчас все выглядит так:
private void UpdateDataGridView(DataSet dataSet)
{
dataGridView1.DataSource = dataSet;
dataGridView1.DataMember = "table1";
}
private void ThreadRead() // Создал поток для чтения xml и выгрузке в datagridview
{
string path = "test.xml";
DataSet ds = new DataSet();
ds.ReadXml(path);
dataGridView1.Invoke(new UpdateDataGridViewCallback(UpdateDataGridView), new object[] { ds });
MessageBox.Show("Данные успешно загружены", "Готово");
}
private void ThreadForm() // Создал поток для показа Form2 (Типа подождите идет загрузка)
{
Form2 frm = new Form2();
frm.Show();
}
private void открытьToolStripMenuItem_Click(object sender, EventArgs e)
{
Thread t2 = new Thread(new ThreadStart(this.ThreadForm));
t2.Start();
Thread t1 = new Thread(new ThreadStart(this.ThreadRead));
t1.Start();
}
private delegate void UpdateDataGridViewCallback(DataSet dataSet); // Делегат для метода заполнения DataGridView.
public delegate void OnWorkIsDoneHandler(object sender, EventArgs e); // Делегат для события окна.
public event OnWorkIsDoneHandler OnWorkIsDone; // Событие окна: завершение потока чтения.
public Form1()
{
InitializeComponent();
// Подписка на событие.
OnWorkIsDone += Form1_OnWorkIsDone;
}
// Обработчик события окончания работы потока чтения.
private void Form1_OnWorkIsDone(object sender, EventArgs e)
{
MessageBox.Show("Загрузка завершена");
}
private void Form1_Load(object sender, EventArgs e)
{
System.Threading.Thread readThread = new System.Threading.Thread(new System.Threading.ThreadStart(ReadThread));
readThread.Start();
}
private void ReadThread()
{
// Инициализация и показ окна.
waitForm = new Form();
InitializeWaitForm();
waitForm.Show();
DataSet ds = new DataSet();
ds.Tables.Add(new DataTable());
System.Threading.Thread.Sleep(3000); // Имитация работы.
dataGridView1.Invoke(new UpdateDataGridViewCallback(UpdateDataGridView), new object[] { ds });
// Закрываем окно с прогресс-баром и оповещаем основное окно о завершении работы.
waitForm.Close();
if (OnWorkIsDone != null)
{
OnWorkIsDone(this, EventArgs.Empty);
}
}
// Настройка окна с прогресс-баром.
private void InitializeWaitForm()
{
waitForm.Width = 350;
waitForm.Height = 200;
waitForm.Location = new Point(400, 250);
}
// Заполнение DataGridView
private void UpdateDataGridView(DataSet dataSet)
{
dataGridView1.DataSource = dataSet;
dataGridView1.DataMember = "table1";
}
Либо как вам говорили выше - модальный диалог.
Всегда поверх основной формы, пока не закроешь. Но и в нем нужна кнопка прервать/выйти. Прервать, если только есть смысл продолжать работу без загрузки данных. Выйти, если смысла продолжать работу нет без этой загрузки.
По началу я думал, есть смысл продолжать, причем параллельно и основную программу и загрузку...
Ну да...я имею ввиду,как сделать контрол открытьToolStripMenuItem_Click неактивным на период загрузки данных и показа формы прогрессбара?
Просто во время загрузки у меня активен контрол меню.
...MenuItem.setEnabled(false);
или
...MenuItem.Enabled = false;
Спрашиваю, потому что в шарпе не силен. Но думаю, что есть или аналогичное.
Это не контрол. Это обработчик события щелчка по пункту меню, т.е. метод, который вызывается по событию. Контрол у Вас, по всей видимости, называется открытьToolStripMenuItem. У него должно быть свойство Enabled. Если передать этому свойству true (это по умолчанию), то пункт меню работает как ожидается. Если false - пункт меню становится "серым" и некликабельным. Остается добавить 2 строчки кода: с блокировкой пункта меню (перед запуском рабочего потока) и разблокировкой (в методе закрытия окна прогресс-бара).