Мне кажется, что вы оперируете хотя бы одним ложным предположением.
1. Вам не нужно вызывать событие ProgressChanged, чтобы иметь отзывчивый пользовательский интерфейс
В своем вопросе вы говорите это:
BackgroundWorker не ответ
потому что может случиться так, что я не понимаю
уведомление о прогрессе, что означает
не будет звонка
Прогресс изменился, так как DoWork является
одиночный вызов внешней функции.
, .
На самом деле, не имеет значения, называете ли вы событие ProgressChanged
или нет . Целью этого события является временная передача управления обратно в поток графического интерфейса, чтобы сделать обновление, которое каким-то образом отражает ход работы, выполняемой BackgroundWorker
. Если вы просто отображаете индикатор прогресса, на самом деле было бы бессмысленно поднимать событие ProgressChanged
на всех . Индикатор выполнения будет продолжать вращаться до тех пор, пока он отображается, потому что BackgroundWorker
выполняет свою работу в отдельном потоке от графического интерфейса пользователя .
(примечание: DoWork
- это событие, которое означает, что это не , а"одиночный вызов внешней функции"; вы можете добавить столько обработчиков, сколько захотите; и каждый из этих обработчиков может содержать столько вызовов функций, сколько ему нужно.)
2. Вам не нужно вызывать Application.DoEvents, чтобы иметь отзывчивый пользовательский интерфейс
Мне кажется, что вы верите, что единственный способ обновления графического интерфейса - это позвонить Application.DoEvents
:
Мне нужно продолжать звонить
Application.DoEvents (); для
индикатор выполнения, чтобы продолжать вращаться.
Это не так в многопоточном сценарии ; если вы используете BackgroundWorker
, графический интерфейс будет продолжать реагировать (в своем собственном потоке), в то время как BackgroundWorker
делает все, что было присоединено к его событию DoWork
. Ниже приведен простой пример того, как это может работать для вас.
private void ShowProgressFormWhileBackgroundWorkerRuns() {
// this is your presumably long-running method
Action<string, string> exec = DoSomethingLongAndNotReturnAnyNotification;
ProgressForm p = new ProgressForm(this);
BackgroundWorker b = new BackgroundWorker();
// set the worker to call your long-running method
b.DoWork += (object sender, DoWorkEventArgs e) => {
exec.Invoke(path, parameters);
};
// set the worker to close your progress form when it's completed
b.RunWorkerCompleted += (object sender, RunWorkerCompletedEventArgs e) => {
if (p != null && p.Visible) p.Close();
};
// now actually show the form
p.Show();
// this only tells your BackgroundWorker to START working;
// the current (i.e., GUI) thread will immediately continue,
// which means your progress bar will update, the window
// will continue firing button click events and all that
// good stuff
b.RunWorkerAsync();
}
3. Вы не можете запустить два метода одновременно в одном потоке
Вы говорите это:
Мне просто нужно позвонить
Application.DoEvents (), чтобы
Marque Progress Bar будет работать, в то время как
рабочая функция работает в главном
нить . , .
То, что вы просите, это просто не реально . «Основной» поток для приложения Windows Forms - это поток GUI, который, если он занят вашим долгосрочным методом, не предоставляет визуальные обновления. Если вы считаете иначе, я подозреваю, что вы неправильно понимаете, что делает BeginInvoke
: он запускает делегата в отдельном потоке . На самом деле, пример кода, который вы включили в свой вопрос для вызова Application.DoEvents
между exec.BeginInvoke
и exec.EndInvoke
, является избыточным; Вы на самом деле звоните Application.DoEvents
несколько раз из потока GUI, , который будет обновляться в любом случае . (Если вы нашли иное, я подозреваю, что это потому, что вы сразу вызвали exec.EndInvoke
, что блокировало текущий поток до завершения метода.)
Так что да, ответ, который вы ищете, это использовать BackgroundWorker
.
Вы можете использовать BeginInvoke
, но вместо вызова EndInvoke
из потока GUI (который заблокирует его, если метод не завершен), передайте параметр AsyncCallback
в BeginInvoke
позвоните (вместо того, чтобы просто передать null
), и закройте форму прогресса в вашем обратном вызове. Имейте в виду, однако, что если вы сделаете это, вам придется вызывать метод, который закрывает форму прогресса из потока GUI, так как в противном случае вы будете пытаться закрыть форму, которая является функцией GUI, из поток без графического интерфейса. Но на самом деле все подводные камни использования BeginInvoke
/ EndInvoke
уже были рассмотрены с для вас с классом BackgroundWorker
, даже если вы думаете, что это «.NET магический код» (для меня, это просто интуитивно понятный и полезный инструмент).