Форма не обновляется после ShowDialog (Compact Framework)

У меня странная проблема с отрисовкой форм в Compact Framework. У меня есть диалоговое окно входа в систему, которое в основном представляет собой небольшую форму, которая открывается поверх другой с помощью ShowDialog. Когда карта перемещается, диалоговое окно входа в систему должно закрываться, затем выполняются некоторые задачи входа в систему, а затем должна быть активирована форма за ним. Проблема в том, что форма за диалоговым окном входа в систему не обновляется, и поэтому диалоговое окно входа в систему не будет удалено до тех пор, пока форма за ним не будет обновлена ​​каким-либо действием пользователя. Вероятно, это связано с тяжелой обработкой, которая выполняется в части задач входа в систему, но я не нашел способа решить эту проблему.

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

Form loginDialog = new Form();
DialogResult result = loginDialog.ShowDialog();
loginDialog.Dispose();

//I've tried everything at this point to get the form to refresh before performing
//login tasks
this.Refresh();
this.Invalidate();
Application.DoEvents();


PerformHeavyLoginTasks();

Кто-нибудь знает, что может пойти не так? Спасибо


person JayPea    schedule 21.09.2010    source источник


Ответы (2)


Хорошо, я понял это. Проблема заключалась в настраиваемом элементе управления в фоновой форме, который вручную раскрашивает себя, используя прямоугольники и тому подобное. Я думаю, что это компактная ошибка фреймворка, поскольку я также вызвал Refresh и Invalidate для этого элемента управления, и он должен был быть перерисован. Мне пришлось создать метод, который бы напрямую вызывал переопределение элемента управления OnPaint, поскольку Invalidate и Refreshed практически игнорировались.

person JayPea    schedule 21.09.2010

Я считаю, что проблема в том, что вы не до конца понимаете, что здесь происходит с точки зрения системы.

Когда ваше переднее окно (диалоговое окно) закрывается, фоновое окно (форма) получает фокус и позволяет перекрашивать область отсечения, где было диалоговое окно. Это происходит через вызов PostMessage, который отправляет сообщение Windows, которое должно быть извлечено, переведено и отправлено в недрах вызова Application.Run.

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

Если вы выполняете тяжелую обработку сразу после того, как происходит PostMessage, обработка этих сообщений Windows часто может замедляться, в результате пользовательский интерфейс выглядит «заблокированным» или отрисовывается очень медленно. Это усугубляется, если выполняемая вами обработка выполняется в том же потоке, что и пользовательский интерфейс.

Почему вы не делаете ничего лучше?

  • Вызов Refresh просто отправляет другое сообщение. Теперь это сообщение поступает в очередь на обработку, так что на самом деле это только ухудшит ситуацию.
  • Вызов Invalidate делает почти то же самое, что и Refresh, только асинхронно. Опять же, это только усугубляет ситуацию.
  • DoEvents указывает насосу сообщений выдавать, переводить и отправлять сообщение. Эта отправка по-прежнему должна обрабатываться в потоке пользовательского интерфейса, поэтому запись будет происходить до тех пор, пока поток не успеет выполнить работу (то есть после вашей обработки).

Так как же это «исправить»?

Первым шагом часто является размещение обработки в отдельном потоке, чтобы планировщик мог выполнять циклические задачи между пользовательским интерфейсом и потоками обработки, вплоть до кванта по умолчанию. Это означает, что обработка может «заморозить» пользовательский интерфейс не более чем на 100 мс, прежде чем будет разрешен какой-либо вид рисования (при условии равного приоритета потока).

new Thread(PerformHeavyLoginTasks)
{
    IsBackground = true
}.Start();

Вы можете пойти дальше и дать пользовательскому интерфейсу «толчок» в обработке (10 мс в этом примере):

new Thread(new ThreadStart(delegate
    {
        Thread.Sleep(10);
        PerformHeavyLoginTasks();
    }))
{
    IsBackground = true
}.Start();

Конечно, это может означать, что теперь вам нужно обрабатывать следующий «дисплей» асинхронно, если пользовательский интерфейс, который вы хотите отобразить, зависит от результата обработки. Есть много онлайн-ресурсов по асинхронным шаблонам, так что я не буду здесь обыгрывать эту мертвую лошадь.

person ctacke    schedule 21.09.2010
comment
Спасибо за ваш ответ, приятно знать, как работает этот процесс, поскольку я в значительной степени догадывался. В этом случае я нашел способ заставить элемент управления рисовать себя перед тем, как перейти к тяжелой обработке, но я уверен, что эта информация пригодится. - person JayPea; 22.09.2010