2015-11-03 4 views
2

Рассмотрите приложение с одним окном.Есть ли способ «задержать» сообщение WM_ACTIVATE/WM_ACTIVATEAPP?

Если окно активировано - то есть, если оконная процедура (WindowProc) возвращает сообщение WM_ACTIVATE (или, аналогично, WM_ACTIVATEAPP) - оно выполняет действие. (Действие, чтобы проверить открытый файл для изменения, если да, то спросить пользователя, если он хочет, чтобы загрузить файл)

Проблема: WM_ACTIVATE/WM_ACTIVATEAPP также посылается, когда пользователь выполняет действия, как нажав закрыть кнопку приложения или переместить окно, перетащив заголовок. Тем не менее, эти сообщения получены до полученных сообщений, таких как WM_CLOSE или WM_MOVE. В этих случаях, очевидно, нужно ждать, пока пользователь не закончит свое действие, прежде чем что-нибудь спросить.

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

+1

'WM_CLOSE' это особый случай, но это кажется глупым, чтобы сломать эту особенность вашего приложения просто потому, что пользователь переместил окно немного. Вы можете сделать «Вы хотите перезагрузить файл?» диалог немодальный, таким образом, пользователь по-прежнему может свободно взаимодействовать с основным окном (и если они его закрывают, вы просто закрываете диалог). –

+1

Точно так же, как точка данных, это меня раздражает, когда приложения делают такие вещи. Я часто меняю окна по той или иной причине, и я не ожидаю, что у вас появятся побочные эффекты. –

+0

Я согласен с @HarryJohnston, с точки зрения пользователя, все, что вы делаете, должно быть неинвазивным, чтобы начать с него. Выявление диалогового окна модели неприемлемо. Я буду ненавидеть ваше программное обеспечение. Найдите более элегантный способ сделать это, например, неинтрузивный оверлей, что-то, что не вызывает мертвой остановки во всем, что я делаю. Как только это неинтрузивно, он сразу же решает всю вашу проблему! – MicroVirus

ответ

0

Как я уже говорил выше, я категорически против использования таймера здесь.

Существуют определенные условия, когда вы не хотите действовать сразу, потому что клиент все еще выполняет какое-либо действие (перемещение, калибровка и т. Д.). Эти действия обычно требуют захвата мыши. Итак, мое предложение:

Отправьте сообщение команды в свое окно, когда оно активируется. В случае его закрытия он никогда не придет к обработке этой команды.

При обработке этого сообщения убедитесь, что вы снимаете мышь, а если вы - установите флаг; проверьте его, когда вы получите сообщение WM_CAPTURECHANGED, и если оно было установлено - отправьте одно и то же сообщение самому себе.

Вот код:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 
{ 
    static bool bWaitForCapture = false; 
    switch (message) 
    { 
    case WM_COMMAND: 
     switch (LOWORD(wParam)) 
     { 
     case WM_APP+1: 
      if ((bWaitForCapture = ::GetCapture() == hWnd) != true) 
       ::OutputDebugString(L"WM_COMMAND : WM_APP+1\n"); 
      break; 
     case IDM_EXIT: 
      DestroyWindow(hWnd); 
      break; 
     default: 
      return DefWindowProc(hWnd, message, wParam, lParam); 
     } 
     break; 
    case WM_DESTROY: 
     PostQuitMessage(0); 
     break; 
    case WM_ACTIVATE: 
     if (wParam != WA_INACTIVE) 
      ::PostMessage(hWnd, WM_COMMAND, WM_APP + 1, 0); 
     break; 
    case WM_CAPTURECHANGED: 
     if (bWaitForCapture) 
      ::PostMessage(hWnd, WM_COMMAND, WM_APP + 1, 0); 
     break; 
    default: 
     return DefWindowProc(hWnd, message, wParam, lParam); 
    } 
    return 0; 
} 
+0

Я вижу потенциальные проблемы с использованием таймера, и на самом деле первая часть вашего ответа работает очень хорошо (с помощью PostMessage один раз поставить командное сообщение в конец очереди). Однако вся логика '' bWaitForCapture '' выглядит принципиально нарушенной: она не выполняет действие после, например, перемещение окна и попадание в бесконечный цикл в противном случае (постоянное получение «WM_CAPTURECHANGED»), что приводит к большим переосмыслениям. Любая идея, как это исправить? –

+0

Этот код отлично подходит для меня. «bWaitForCapture» является статическим для этого WndProc, он устанавливается/восстанавливается только при приеме команды «WM_APP + 1», в зависимости от того, будет ли ваше окно захвачено в это время (что означает «перетаскивание» или «повторная калибровка»). «WM_CAPTURECHANGED» появляется, когда захват, естественно, изменяется, и я только повторно отправляю команду 'WM_APP + 1', если вы захватили. Вы случайно тестируете под отладчиком, с точкой останова на 'WM_CAPTURECHANGED'? Это будет проблемой, поскольку Debugger заставит вас разорвать захват, заставив вас снова захватить снова и снова. –

+0

На этом этапе я думаю, что ваш код _should_ работает в принципе, и поэтому я буду принимать его в качестве ответа, поскольку он более изящный, чем использование таймера. Однако я все еще не могу заставить его работать, поскольку что-то кажется сломанным, как только я повторно отправляю сообщение «WM_ACTIVATE» так или иначе (та же проблема с таймером), вместо того, чтобы немедленно его обрабатывать (я открываю модальный dalog спрашивая пользователя, хочет ли он перезагрузить, но в этот момент курсор мыши начинает мерцать ...). –

2

Я не думаю, что вы можете отложить это сообщение, так как последовательность сообщений определяется Windows.

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

Следующий фрагмент кода показывает, как реализовать это:

#define IDT_UPDATE_TIMER 1000 


UINT_PTR timerId = NULL; /* needs to be stored along with other window data */ 

switch (uMsg) 
{ 
    WM_ACTIVATE: 
    WM_ACTIVATEAPP: 
     /* create timer to delay checking for file updates... */ 
     timerId = SetTimer(hWnd, IDT_UPDATE_TIMER, 50, NULL); 
     break; 

    WM_CLOSE: 
     /* cancel timer since window is being closed */ 
     KillTimer(hWnd, timerId); 
     break; 

    WM_TIMER: 
     switch (wParam) 
     { 
     case IDT_UPDATE_TIMER: 
      /* cancel timer to avoid retesting the file every 50ms */ 
      KillTimer(hWnd, timerId); 

      /* check for file updates... */ 
      break; 
     } 
     break; 
} 
+0

Должен был дать этот голос, похоже, мы пришли к такому же выводу! –

+1

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

+0

@ VladFeinstein: Это правда, но я использую таймер только для того, чтобы отложить проверку обновлений файлов до тех пор, пока вы не получите дальнейшие сообщения и не узнаете, почему было активировано ваше окно. Поэтому, если вы получили сообщение 'WM_MOVE' после' WM_ACTIVATE', вам нужно сбросить (!!! не отменить !!!) таймер. Поэтому после того, как вы перестанете перемещать окно (и больше не получаете сообщений WM_MOVE), таймер сброса истекает, и файл проверяется на наличие обновлений (в моем случае 50 мс после того, как вы остановили перемещение окна). То же самое нужно сделать для «WM_SIZE» и всех других сообщений, связанных с действиями пользователя ... –

0

Хороший вопрос!

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

Что вы можете сделать, однако, это изменить способ использования этих сообщений, возможно, использование таймера. Вы должны представить себе цепочку возможных событий.

Одним из способов может быть создание класса для обработки этого. Он обрабатывает WM_ACTIVATE/WM_ACTIVATEAPP. Как только он получит это сообщение, запустите таймер. Если вы получите WM_MOVE, отрегулируйте его, сбросьте таймер.

Если вы запустите свой код активации, то вызовите окно окна по умолчанию или позвоните по номеру DestroyWindow.

Если таймер истекает (половина секунды?Возможно, вам придется играть с точным порогом), тогда вы можете сделать свой код активации.

Вот что я имею в голове и извиняюсь, если я не очень хорошо себя объяснил.

+0

Таймер - это очень неряшливое решение в лучшем случае. Возможно, возможно, единственное решение для сообщений. В этом случае я очень рекомендую немодельный диалоговый вид решения. – MicroVirus

Смежные вопросы