#c# #winforms
#c# #winforms
Вопрос:
Когда я использую метод делегирования для запуска моего метода, чтобы позволить обновиться пользовательскому интерфейсу, пользовательский интерфейс не обновляется. Я хочу остановить зависание пользовательского интерфейса во время выполнения метода, что я могу сделать?
private void SearchToolStripButton_Click(object sender, EventArgs e)
{
Thread t = new Thread(new ThreadStart(StartMethod));
t.Start();
}
private delegate void InvokeDelegate();
public void StartMethod()
{
this.BeginInvoke(new InvokeDelegate(SearchData));
}
public void SearchData()
{
if (searchKeywordTextBox.Text == "")
{
MessageBox.Show("Please type the keyword!");
}
else
{
if (searchDateTimePicker.Checked == true)
{
switch (selectRangeComboBox.Text)
{
case "Day": showDataToresultDataGridViewOnDay(searchKeywordTextBox.Text);
break;
case "Month": showDataToresultDataGridViewInMonth(searchKeywordTextBox.Text);
break;
case "Year": showDataToresultDataGridViewInYear(searchKeywordTextBox.Text);
break;
default: MessageBox.Show("Please select a Section");
break;
}
}
else
{
showDataToresultDataGridView(searchKeywordTextBox.Text);
}
}
}
public void showDataToresultDataGridViewOnDay(string keyword)
{
DataGridView dayGrid = resultDataGridView;
ShowResultDay day = new ShowResultDay();
resultDataGridView.DataSource = day.ShowGridDay(searchDateTimePicker.Value.Day, searchDateTimePicker.Value.Month, searchDateTimePicker.Value.Year, keyword);
resultLabel.Text = "Showing " resultDataGridView.RowCount " records in " searchDateTimePicker.Value.Day "/" searchDateTimePicker.Value.Month "/" searchDateTimePicker.Value.Year;
}
public void showDataToresultDataGridViewInMonth(string keyword)
{
DataGridView monthGrid = resultDataGridView;
ShowResultMonth month = new ShowResultMonth();
resultDataGridView.DataSource = month.ShowGridMonth(searchDateTimePicker.Value.Month, searchDateTimePicker.Value.Year, keyword);
resultLabel.Text = "Showing " resultDataGridView.RowCount " records in " searchDateTimePicker.Value.Month "/" searchDateTimePicker.Value.Year;
}
public void showDataToresultDataGridViewInYear(string keyword)
{
DataGridView yearGrid = resultDataGridView;
ShowResultYear year = new ShowResultYear();
resultDataGridView.DataSource = year.ShowGridYear(searchDateTimePicker.Value.Year, keyword);
resultLabel.Text = "Showing " resultDataGridView.RowCount " records in " searchDateTimePicker.Value.Year;
}
public void showDataToresultDataGridView(string keyword)
{
ShowAllData all = new ShowAllData();
var results = all.ShowGirdAll(keyword);
resultDataGridView.DataSource = results;
resultLabel.Text = "Showing " resultDataGridView.RowCount " records";
}
Комментарии:
1. Вы понимаете, как работает BeginInvoke? Бесполезно вызывать StartMethod из фонового потока, если все, что он делает, — это планирует обратный вызов SearchData в потоке пользовательского интерфейса. Даже если вы создали фоновый поток, это бессмысленно, потому что вы все еще выполняете всю работу в потоке пользовательского интерфейса. Независимо от того, как вы его разделите, вам нужно будет отделить код пользовательского интерфейса от фонового кода, если вы хотите, чтобы они выполнялись в разных потоках.
Ответ №1:
Вам следует взглянуть на BackgroundWorker
класс, который был реализован специально для упрощения многопоточности в WinForms.
Тогда я бы предложил другое:
не открывайте ящики сообщений из фонового потока, но проверьте свойства и поля перед запуском потока, а затем запустите только поиск в потоке, отличном от пользовательского интерфейса.
Пример использования фонового рабочего:
// Initialization
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork = new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted = new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
// Start elaboration
bw.RunWorkerAsync(objectArgument);
void bw_DoWork(object sender, DoWorkEventArgs e)
{
// do your work (we are in the background-thread)
// when you have finished, set your results in the e.Result property
// N.B. don't show anything because we are in the background-thread
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// here we have finished the work (we are in the UI-thread)
// result is set in e.Result property
// N.B. check e.Error property before to get e.Result because
// if there's an error e.Result throws an exception
}
Ответ №2:
В отличие от встроенного Win32 API или MFC, платформа Dot-net Framework не поддерживает многопоточные вызовы метода пользовательского интерфейса * для элементов управления . Таким образом, вы не можете создать элемент управления пользовательского интерфейса (window) ни в одном потоке и вызвать его метод и задать свойство из другого! (*здесь и ниже — методы пользовательского интерфейса, которые обновляют) Контекст пользовательского интерфейса. Вы можете считывать данные из элемента управления и не можете записывать в) Кстати, в родной среде такие вызовы тоже работают ненадежно, иногда вызывая плохо управляемое исключение … Но управляемая среда не может разрешить, чтобы вызов метода вообще вызывал исключение! Он просто «замораживает» такой вызов и ничего не делает для обновления. Кстати, эта функция платформы предназначена не только для фоновых потоков . Независимо от того, вызываете ли вы BeginInvoke (из встроенного пула потоков) или создаете пользовательский нефоновый поток другим способом, вы столкнетесь с той же проблемой. В вашем конкретном случае я не вижу никаких причин для того, чтобы два или более потоков совместно использовали один и тот же элемент управления — это также опасно с точки зрения безопасности данных. Как правило, другие потоки (и вообще параллелизм) используются для совершенно других целей: для любых длительных вычислений, операций ввода-вывода баз данных, файлов, сети. Хотя, конечно, вы можете вызвать некоторые окна сообщений в другом потоке .