Выбрать главу

Если длительная задача выполняется в высокоприоритетном потоке (том же, в котором выполняется код пользовательского интерфейса), то предоставление пользователю возможности отменить ее выполнение, хотя и осложняется, по-прежнему остается осуществимым. Как и в случае обновления пользовательского интерфейса в процессе выполнения интенсивных вычислений, реакция на пользовательские запросы, в том числе и на щелчок на кнопке отмены выполнения, возможна лишь при условии обработки сообщений потока пользовательского интерфейса. В .NET Compact Framework предусмотрено решение для подобных ситуаций, но аналогичные концепции используются, вероятно, и в каркасах приложений для других устройств. Вызов статического метода System.Windows.Forms.Application.DoEvents() приводит к принудительной обработке всех сообщений, находящихся в очереди данного потока, до того, как выполнение сможет быть продолжено. Это означает, что будут обработаны и сообщения, требующие перерисовки пользовательского интерфейса, в том числе ожидающие обработки щелчки на кнопках и нажатия клавиш. Если вызывать метод DoEvents() достаточно часто (несколько раз в секунду), то можно обеспечить сохранение интерактивности пользовательского интерфейса без малейшего ущерба для выполнения текущей задачи.

Может показаться, что метод DoEvents() является панацеей от всех бед, но это не так. Вызывая метод DoEvents(), необходимо проявлять осторожность, поскольку при этом могут порождаться в высшей степени нежелательные тонкие эффекты. Так как вызов метода DoEvents() приводит к обработке всех сообщений, прежде чем управление будет возвращено вызвавшему его коду, то это может сопровождаться вызовом обработчиков событий таймера и повторными вхождениями в выполняющиеся в данный момент обработчики событий. В результате этого может сложиться такая ситуация, при которой мы будем иметь множество вложенных вызовов обработчиков событий, связанных с элементами пользовательского интерфейса, и таймеров, если метод DoEvents() вызывается внутри обработчика события и новое сообщение о событии помещается в очередь еще до завершения обработки первого события. Собираясь использовать метод DoEvents(), вы должны использовать при написании кода обработчиков событий своего приложения технологию "безопасного программирования" (defensive programming), которая предполагает тщательную проверку выполнения всех допустимых условий. Настоятельно рекомендуется помещать все подобные проверки в самом начале кода обработчиков событий, чтобы осуществить немедленный выход из функции в случае, если вхождение в нее является повторным. Прежде чем осуществить вхождение в блок кода, в котором выполняется задача, требующая длительного времени, и в процессе этого вызывается метод DoEvents(), убедитесь в том, что для всех элементов управления, генерация которыми событий должна быть запрещена, значение свойства Enabled установлено в false; благодаря этой мере пользователи будут лишены возможности выполнять щелчки на указанных элементах управления, тем самым загромождая очередь событий. Ситуации, в которых возможность повторного вхождения в обработчики событий является желательной, встречаются крайне редко, ибо в этом случае код становится чрезвычайно запутанным и трудно поддается отладке. Если вы можете выбирать между использованием метода DoEvents() и созданием фонового потока для выполнения основных вычислений, то я бы рекомендовал почти всегда отдавать предпочтение хорошо продуманному последнему решению. Каким бы сложным с концептуальной точки зрения ни казалось использование нескольких потоков, модель их выполнения обычно оказывается более простой и предсказуемой, чем смесь кодов каркаса и приложения, вызываемая при использовании метода DoEvents(). Ничто из вышесказанного не является чем-то специфическим для мобильных устройств; те же самые проблемы возникают и в случае настольных компьютеров, однако мы акцентируем на них внимание, исходя из повышенных потребностей конечных пользователей в отношении интерактивного взаимодействия с приложением в случае мобильных устройств. Тем не менее, иногда применение метода DoEvents() может оказаться полезным, и я описываю здесь этот подход, одновременно предостерегая вас: "Caveat emptor!" ("Пусть покупатель будет бдителен!"), то есть действуйте на свой страх и риск. Если вы покупаете билет на DoEvents(), то должны быть готовы ко всем подъемам и спускам на этом маршруте.