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

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

  • Кнопка кликов
  • Метод проверяет наличие обновлений, возвращается количество.
  • Если больше 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

Ответов (3)

Решение

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

this.BeginInvoke((MethodInvoker)delegate() {update.Action.Run(); }) 

говорит о вызове update.Action.Run () в потоке, который создал «this» (вашу форму), который является потоком пользовательского интерфейса.

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