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

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

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

6 голосов
/ 13 августа 2008

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

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

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

Application.DoEvents()

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

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

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

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

1 голос
/ 22 августа 2008

@ Джон Симбл

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

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

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

1 голос
/ 13 августа 2008

Вы пробовали поставить

Application.DoEvents()

здесь

if (dlgRes == DialogResult.Yes)
{
   Application.DoEvents(); 
   ProcessAllUpdates(um2); 
}
...