Compact Framework/Threading — MessageBox отображается поверх других элементов управления после выбора параметра.

StackOverflow https://stackoverflow.com/questions/10071

Вопрос

Я работаю над приложением, которое захватывает и устанавливает кучу обновлений с внешнего сервера, и мне нужна помощь с потоковой передачей.Пользователь следует этому процессу:

  • Нажимает кнопку
  • Метод проверяет наличие обновлений, возвращается счетчик.
  • Если значение больше 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(); });
}

Скриншот

Windows Mobile Bug

Это было полезно?

Решение

Ваш пользовательский интерфейс не обновляется, поскольку вся работа происходит в потоке пользовательского интерфейса.Ваш звонок:

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, вы должны предполагать, что он будет протекать, как вы и сказали.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top