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

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

  • Кнопка кликов
  • Метод проверяет наличие обновлений, возвращается счетчик.
  • Если больше 0, спросите пользователя, хотят ли они установить с помощью MessageBox.Show ().
  • Если да, он проходит через цикл и вызывает метод BeginInvoke () для метода run () каждого обновления, чтобы запустить его в фоновом режиме.
  • В моем классе обновлений есть некоторые события, которые используются для обновления индикатора выполнения и т. Д.

Обновления индикатора выполнения хороши, но MessageBox не полностью очищен от экрана, потому что цикл обновления запускается сразу после того, как пользователь нажимает да (см. Скриншот ниже).

  • Что я должен сделать, чтобы окно сообщений мгновенно исчезло до начала цикла обновления?
  • Должен ли я использовать потоки вместо 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

13.08.2008 16:55:51
3 ОТВЕТА
РЕШЕНИЕ

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

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

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

Application.DoEvents()

действительно даст потоку пользовательского интерфейса возможность перерисовать экран, но у меня будет соблазн создать новый делегат и вызвать для этого BeginInvoke.

Это выполнит функцию update.Action.Run () в отдельном потоке, выделенном из пула потоков. Затем вы можете продолжать проверять IAsyncResult до тех пор, пока обновление не будет завершено, запрашивая объект обновления на предмет его прогресса после каждой проверки (потому что другой поток не может обновить индикатор выполнения / пользовательский интерфейс), затем вызывая Application.DoEvents ().

Вы также должны вызывать EndInvoke () впоследствии, иначе вы можете получить утечку ресурсов

Я также хотел бы вставить кнопку отмены в диалоговое окно прогресса и добавить тайм-аут, в противном случае, если обновление застрянет (или займет слишком много времени), ваше приложение будет заблокировано навсегда.

6
13.08.2008 18:17:23

Вы пытались положить

Application.DoEvents()

здесь

if (dlgRes == DialogResult.Yes)
{
   Application.DoEvents(); 
   ProcessAllUpdates(um2); 
}
1
13.08.2008 17:37:08

@ Джон Сибли

Вы можете уйти с не вызывая EndInvoke при работе с WinForms без каких - либо негативных последствий.

Единственное документированное исключение из правила, о котором я знаю, - это Windows Forms, где вам официально разрешено вызывать Control.BeginInvoke, не удосуживаясь вызвать Control.EndInvoke.

Однако во всех других случаях, когда вы работаете с шаблоном Begin / End Async, вы должны предполагать, что он утечет, как вы заявили.

1
21.08.2008 20:21:42