Почему Refresh () не делает то, что делает DoEvents ()? - PullRequest
0 голосов
/ 04 января 2012

Я пытаюсь понять некую давнюю концепцию в Windows Forms: программирование пользовательского интерфейса; следующий код взят из книги Криса Селлса по программированию Windows Forms (2nd Ed., 2006):

void ShowProgress(string pi, int totalDigits, int digitsSoFar) {
  // Display progress in UI
  this.resultsTextBox.Text = pi;
  this.calcToolStripProgressBar.Maximum = totalDigits;
  this.calcToolStripProgressBar.Value = digitsSoFar;

  if( digitsSoFar == totalDigits ) {
    // Reset UI
    this.calcToolStripStatusLabel.Text = "Ready";
    this.calcToolStripProgressBar.Visible = false;
  }

  // Force UI update to reflect calculation progress
  this.Refresh();
}

Этот метод является частью небольшого примера приложения, у которого есть другой длительный метод, который вычисляет Pi. Каждый раз, когда вычисляется кластер цифр, вызывается ShowProgress () для обновления пользовательского интерфейса. Как объясняется в книге, этот код является «неправильным» способом выполнения действий и заставляет пользовательский интерфейс зависать, когда приложение сворачивается, а затем снова выводится на передний план, в результате чего система просит приложение перерисовать себя.

Что я не понимаю: Поскольку this.Refresh () вызывается неоднократно, почему он не обрабатывает событие системного перерисовки, которое ожидает внимания?

И дополнительный вопрос: Когда я добавляю Application.DoEvents () сразу после этого. Refresh (), проблема с зависанием исчезает. Это без необходимости прибегать к Invoke / BeginInvoke, и т.д. Любые комментарии?

1 Ответ

5 голосов
/ 04 января 2012

По сути, причина в том, что Windows обрабатывает сообщения - она ​​делает это синхронно во внутреннем цикле сообщений.

Дело в том, что было сообщение, котороесработал ваш код.Например, нажатие кнопки.Ваше приложение находится в процессе обработки сообщения.Из этого обработчика вы вызываете обновление, которое помещает еще один WM_PAINT в очередь сообщений.Когда ваш обработчик завершит работу, цикл обработки сообщений обязательно его заберет и отправит, таким образом, перерисовав элемент управления.Но ваш код не завершен, фактически он вызывает ваш ShowProgress, вызывая то, что WM_PAINT ставится в очередь навсегда.

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

внешний цикл сообщений -> ваш код -> внутренний цикл сообщений.

внутреннее сообщениеЦикл обрабатывает все ожидающие сообщения, включая WM_PAINT (таким образом, элемент управления перерисовывается), но это опасно - так как он будет отправлять все другие ожидающие сообщения , включая нажатия кнопок, нажатия меню или события, закрывающие ваше приложение с Xв правом верхнем углу.К сожалению, нет простого способа сделать цикл для обработки только WM_PAINT, что означает, что вызов DoEvents () подвергает ваше приложение тонким потенциальным проблемам, связанным с неожиданной пользовательской активностью во время выполнения вашего кода, который вызывает DoEvents.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...