Compact Framework/Threading — MessageBox отображается поверх других элементов управления после выбора параметра.
-
08-06-2019 - |
Вопрос
Я работаю над приложением, которое захватывает и устанавливает кучу обновлений с внешнего сервера, и мне нужна помощь с потоковой передачей.Пользователь следует этому процессу:
- Нажимает кнопку
- Метод проверяет наличие обновлений, возвращается счетчик.
- Если значение больше 0, спросите пользователя, хотят ли они установить его, используя MessageBox.Show().
- Если да, он запускается в цикле и вызывает BeginInvoke() в методе run() каждого обновления, чтобы запустить его в фоновом режиме.
- В моем классе обновления есть некоторые события, которые используются для обновления индикатора выполнения и т. д.
Обновления индикатора выполнения в порядке, но MessageBox не полностью удаляется с экрана, поскольку цикл обновления начинается сразу после того, как пользователь нажимает «Да» (см. снимок экрана ниже).
- Что мне следует сделать, чтобы окно сообщения исчезло мгновенно до начала цикла обновления?
- Должен ли я использовать Threads вместо BeginInvoke()?
- Должен ли я выполнять первоначальную проверку обновлений в отдельном потоке и вызывать MessageBox.Show() из этого потока?
Код
// Button clicked event handler code...
DialogResult dlgRes = MessageBox.Show(
string.Format("There are {0} updates available.\n\nInstall these now?",
um2.Updates.Count), "Updates Available",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2
);
if (dlgRes == DialogResult.Yes)
{
ProcessAllUpdates(um2);
}
// Processes a bunch of items in a loop
private void ProcessAllUpdates(UpdateManager2 um2)
{
for (int i = 0; i < um2.Updates.Count; i++)
{
Update2 update = um2.Updates[i];
ProcessSingleUpdate(update);
int percentComplete = Utilities.CalculatePercentCompleted(i, um2.Updates.Count);
UpdateOverallProgress(percentComplete);
}
}
// Process a single update with IAsyncResult
private void ProcessSingleUpdate(Update2 update)
{
update.Action.OnStart += Action_OnStart;
update.Action.OnProgress += Action_OnProgress;
update.Action.OnCompletion += Action_OnCompletion;
//synchronous
//update.Action.Run();
// async
IAsyncResult ar = this.BeginInvoke((MethodInvoker)delegate() { update.Action.Run(); });
}
Скриншот
Решение
Ваш пользовательский интерфейс не обновляется, поскольку вся работа происходит в потоке пользовательского интерфейса.Ваш звонок:
this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); })
говорит: вызовите update.Action.Run() в потоке, который создал «это» (вашу форму), который является потоком пользовательского интерфейса.
Application.DoEvents()
действительно даст потоку пользовательского интерфейса возможность перерисовать экран, но у меня возникнет соблазн создать новый делегат и вызвать для него BeginInvoke.
Это выполнит функцию update.Action.Run() в отдельном потоке, выделенном из пула потоков.Затем вы можете продолжать проверку IAsyncResult до тех пор, пока обновление не будет завершено, запрашивая объект обновления о его ходе после каждой проверки (поскольку вы не можете заставить другой поток обновлять индикатор выполнения/пользовательский интерфейс), а затем вызывать Application.DoEvents().
Вы также должны впоследствии вызвать EndInvoke(), иначе вы можете столкнуться с утечкой ресурсов.
У меня также возникнет соблазн разместить кнопку отмены в диалоговом окне выполнения и добавить тайм-аут, иначе, если обновление застрянет (или займет слишком много времени), ваше приложение заблокируется навсегда.
Другие советы
Вы пробовали поставить
Application.DoEvents()
здесь
if (dlgRes == DialogResult.Yes)
{
Application.DoEvents();
ProcessAllUpdates(um2);
}
@ Джон Сибли
Вы можете уйти с нет вызов EndInvoke при работе с WinForms без каких-либо негативных последствий.
Единственное задокументированное исключение из правила, о котором мне известно, — это Windows Forms, где официально разрешено вызывать Control.BeginInvoke, не утруждая себя вызовом Control.EndInvoke.
Однако во всех других случаях, когда вы имеете дело с шаблоном Begin/End Async, вы должны предполагать, что он будет протекать, как вы и сказали.