Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Как убить не модальный property sheet?


Автор: Royan 24.8.2004, 21:55
Создал modeless PropertySheet:

PROPSHEETHEADER psh = {0}; //
psh.dwFlags = PSH_USECALLBACK | PSH_MODELESS;

Но нажимая на славную кнопку с крестиком или на кнопку OK или на Cancel не могу отловить ни одного сообщения, то есть фактически у меня нет шансов определить нажал ли пользователь на какую-нибудь из этих кнопок или нет.

Кто-нибудь знает как с этим сладить?

Автор: Royan 26.8.2004, 17:31
Оказывается способ совсем не тривиальный. И варианта на самом деле два. Первый весьма ограничен в силу того, что приходится всегда держать информацию о HWND вашего PropertySheet в месте доступном из WinMain(). Вот его применение:

Цитата
//Если же пришедшее сообщение не является сообщением для
//Porperty Sheet, то классически обрабатываем сообщение
if(!PropSheet_IsDialogMessage(hwndPropSheet, &msg))
{
  TranslateMessage(&msg);
  DispatchMessage(&msg);
}
 

//Ели макрос PropSheet_GetCurrentPageHwnd() возвратил NULL значит пользователь
//нажал на OK или Cancel
if(hwndPropSheet && (PropSheet_GetCurrentPageHwnd(hwndPropSheet))==NULL)
{
 
//Выполняем какие-то действия
  //и разрушаем окно наших пропертей
 
DestroyWindow(hwndPropSheet);
  hwndPropSheet = NULL;
}


Весь код разумеется распологается внутри WinMain, что неудобно

Поэтому вот другой подход. Внедрить отдельную функцию обработки сообщений от вашего немодального PropertySheet:
код функции может варироваться от навороченности вашей реализации, но вот примелемый пример:
Цитата
MSG msg;
BOOL bret;

while ((bret = GetMessage (&msg, NULL, 0, 0)) != 0)
{
 
if(bret == -1)
  {
   
//Какой-нить хендлер ошибки
   
return false;
  }

 
if(!PropSheet_IsDialogMessage (GetHWND(), &msg))
  {
    TranslateMessage (&msg);
    DispatchMessage (&msg);
  }

 
if (PropSheet_GetCurrentPageHwnd(GetHWND()) == NULL)
  { 
   
// Собственно выходим из цикла
   
break;
  }
}

Автор: AndyY 26.8.2004, 20:58
Royan
что-то нифига не понял, зачем такие сложности.
Не проще ли отсубклассить окно propertysheet, обработать IDOK и IDCANCEL и вызвать DEstroyWindow из функции окна. Субклассить propertysheet легко через callback
Добавлено @ 20:58
вложенных циклов сообщений по возможности лучше избегать. Уж слишком часто они являются причиной ошибки.

Автор: stab 26.8.2004, 22:30
AndyY, "отсубклассить" тут не поможет, т.к. до окна не доходят сообщения, для этого все эти навороты и нужны...

Автор: Royan 30.8.2004, 15:00
2AndyY, проблема еще не только в том, что IDOK и IDCANCEL не передадутся через WM_COMMAND, а еще и в том что ID кнопок OK Cancel и Apply тривиально выяснить нельзя, так как они создаются автоматически после вызова PropertySheet(&ps).

2cully, а ты пробовал сабклассировать окошко propertysheet хотя бы из любопытства не проходит ли туда WM_COMMAND с ID остальных элементов?

Автор: AndyY 30.8.2004, 15:23
cully
WM_COMMAND до окна PropertySheet доходит. Естественно, до "внешнего" а не до tabctrl. Да и как может не доходить, если кнопка там самая обычная на окне. Чудес не бывает.


Royan
ID кнопки OK - IDOK
ID кнопки Сancel - IDCANCEL

id остальных кнопок:
#define IDD_APPLYNOW 0x3021
#define IDD_DLGFRAME 0x3022
#define IDD_BACK 0x3023
#define IDD_NEXT 0x3024
#define IDD_FINISH 0x3025
#define IDD_DIVIDER 0x3026
#define IDD_TOPDIVIDER 0x3027
зашиты жестко и никогда не меняются.

Добавлено @ 15:25
единственная сложность в субклассе - то, что в callback функцию нельзя передать параметр (обычно туда складывают указатель на класс). Поэтому приходится либо делать callback-thunk, либо временно сохранять указатель в глобальной переменной.

Автор: Royan 30.8.2004, 17:07
AndyY А что такое callback-thunk?

Цитата
единственная сложность в субклассе - то, что в callback функцию нельзя передать параметр (обычно туда складывают указатель на класс)


Собственно я пользуюсь ::SetWindowLongPtr() и никгода проблем не было.

Автор: AndyY 30.8.2004, 17:50
суть конечно в этом.
но callback для PSH_USECALLBACK не поддерживает передачу параметра.
это важно, если нужно ассоциировать HWND с экземпляром класса.

поэтому указатель на объект можно сохранять в глобальной переменной, откуда читать из callback.
другой путь - сделать callback в виде участка памяти содержащего инструкции:

{
call realHookProc
ret

HWND hwdn;
}

в realHookProc можно получить значение HWND (через SP на старте функции - offset от адреса возврата известен)
эдакий кривой способ передачи параметров

Автор: Royan 30.8.2004, 18:10
Я не понял при зачем так извращаться, когда я всегда могу сделать так:

hwndMain = (HWND)PropertySheet(&ps);

wndprcPointer = ::GetWindowLongPtr(hwndMail, GWLP_WNDPROC);

а wndprcPointer уже будет отрабатывать уже в твоей процедуре обработки сообещиний, которую ты же забъешь через SetWindowLongPtr() hehe.gif

Автор: AndyY 30.8.2004, 20:34
Royan
согласен. Для немодального окна - вполне вариант. Хотя в некоторых случаях это позновато - окно например может быть уже показано в момент когда функция возвратит управление.

Я просто делал универсальный класс для модального и немодального окна.

Идея, почему могут не доходить IDOK и IDCANCEL:
их кушает обработчик сообщения PSM_ISDIALOGMESSAGE. Я использовал просто IsDialogMessage.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)