Лист свойств в диалоге с использованием примера WINAPI (без использования MFC)
Может кто-нибудь указать мне на пример WINAPI встраивания листа свойств в диалоговое окно с использованием WINAPI (не MFC)?
2 ответа
Для справки, вот некоторые из вещей, которые я узнал, исследуя этот вопрос. Я получил большую часть всего этого, задавая вопросы здесь или выполняя поиск в Интернете. Спасибо всем за помощь!
Я написал класс для инкапсуляции всего, что обнаружил; Если вы заинтересованы в получении копии, пришлите мне письмо по электронной почте mdorl@wisc.edu.
Передайте HWND родительского окна для вашего листа свойств в.hwndParent элемента PROPSHEETHEADER при создании листа свойств с помощью функции страницы PropertySheet.
Я использовал элемент управления рисунком в диалоговом окне в качестве родителя листа свойств, поэтому это HWND, который я использовал в качестве hwndParent.
Измените стиль таблицы свойств с помощью поля обратного вызова pfnCallback в заголовке, не забудьте включить PSH_USECALLBACK в dwFlags. См. Функция PropSheetProc в MSDN. В обработчике обратного вызова PSCB_PRECREATE отрегулируйте стиль окна листа свойств, удалив WS_POPUP и добавив WS_CHILD. Я не хотел, чтобы на листе свойств была рамка или строка заголовка, поэтому я также удалил WS_CAPTION, WS_SYSEMNU и DS_MODALFRAME
LONG L = ((LPDLGTEMPLATE)lParam)->style;
L &= ~WS_POPUP;
L &= ~WS_CAPTION; // gets rid of title bar
L &= ~WS_SYSMENU;
L &= ~DS_MODALFRAME; // gets rid of border
L |= WS_CHILD; // 40000000
((LPDLGTEMPLATE)lParam)->style = L;
Использование дочернего окна вместо всплывающего окна аккуратно решает некоторые проблемы, такие как перемещение листа свойств при перемещении родительского элемента, сохранение правильного цвета сине-серого состояния родительского окна и проблемы с отсечкой. Т.е. вам не нужно беспокоиться об этих вещах.
Если вы остановитесь здесь, вы столкнетесь с другой проблемой. Код поддержки диалога в WIN32 попадет в цикл процессора, пытаясь найти таблицу свойств и элементы управления в своей Windows. Увидеть
Для чего нужны WS_EX_CONTROLPARENT и DS_CONTROL?
для довольно хорошего объяснения этой проблемы. Решение состоит в том, чтобы добавить расширенный стиль WS_EX_CONTROLPARENT на страницу свойств. Вы можете найти информацию об этом в Интернете, выполнив поиск WS_EX_CONTROLPARENT. То, что вы не найдете, вызвало у меня много горя. Если вы используете элемент управления в диалоге в качестве родительского, вы также должны установить его WS_EX_CONTROLPARENT.
Я не знаю, в чем разница между WS_EX_CONTROLPARENT и DS_CONTROL, и не знаю, можно ли заменить DS_CONTROL на WS_EX_CONTROLPARENT. Из Интернета я понял, что DS_CONTROL как-то связан с вкладками. Мое тестовое приложение работает одинаково в отношении вкладок с DS_CONTROL или без него.
Я позаботился о бизнесе WS_EX_CONTROLPARENT после вызова функции PropertySheet для создания листа свойств. Я не знаю, можете ли вы сделать это в процедуре обратного вызова или нет. Я полагаю, вы могли бы использовать GetParent при обратном вызове, чтобы получить HWND элемента управления.
LONG S;
S = GetWindowLong (hwndPS, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS, GWL_EXSTYLE, S);
S = GetWindowLong (hwndPS_Area, GWL_EXSTYLE) | WS_EX_CONTROLPARENT;
SetWindowLong (hwndPS_Area, GWL_EXSTYLE, S);
После создания листа свойств с помощью функции PropertySheet его необходимо правильно расположить:
SetWindowPos(hwndPS, HWND_TOP, 2, 2, -1, -1,
SWP_NOSIZE | SWP_NOACTIVATE);
Я позволил пару пикселей для границы.
Если вы хотите избавиться от всех кнопок листа свойств и занимаемого ими пространства:
RECT rectWnd;
RECT rectButton;
GetWindowRect(hwndPS, &rectWnd);
HWND hWnd = ::GetDlgItem(hwndPS, IDOK);
if(!hWnd)
{
DebugBreak();
}
GetWindowRect(hWnd, &rectButton);
SetWindowPos (hwndPS, NULL, 0, 0,
rectWnd.right - rectWnd.left, rectButton.top - rectWnd.top,
SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_SHOWWINDOW);
Отдельные кнопки могут быть исключены:
hwndOk = GetDlgItem (hwndPS,IDOK); // Hide the OK button
ShowWindow (hwndOk, SW_HIDE);
EnableWindow (hwndOk, FALSE);
Вы можете избавиться от кнопки APPLY, используя dwFlags PSH_NOAPPLYNOW в PROPERTYSHEETHEADER, когда вы создаете лист свойств.
Страницы листа свойств НЕ будут создаваться, пока пользователь не активирует их в первый раз. Я хотел, чтобы все они были созданы при создании листа свойств.
unsigned int iP;
for (iP=0; iP<Header.nPages; iP++)
{
if (iP != Header.nStartPage)
SendMessage (hwndPS, PSM_SETCURSEL,iP,NULL);
}
SendMessage (hwndPS, PSM_SETCURSEL,Header.nStartPage,NULL);
Есть флаг PROPSHEETPAGE, который делает то же самое (из MSDN)
PSP_PREMATURE Версия 4.71. Вызывает создание страницы при создании листа свойств. Если этот флаг не указан, страница не будет создана, пока она не будет выбрана в первый раз.
Но я беспокоился о версии 4.71, поэтому я сделал это сам.
Вы можете изменить текст на любую из кнопок:
SetDlgItemText (hwndPS,ButtonID,Text);
где ButtonID - это один из IDOK IDCANCEL IDHELP IDAPPLYNOW
IDAPPLYNOW не определен в моей системе, поэтому
#define IDAPPLYNOW 0x3021
Вот как я собираюсь использовать свой список свойств. Я собираюсь поместить весь код инициализации и завершения в диалоговое окно, содержащее лист свойств, и исключить все кнопки листа свойств. Я могу установить все начальные значения и получить все конечные значения, когда пользователь нажимает какую-либо кнопку DO IT. Я замечаю, что в результате установки, например, текстового поля, диалоговое окно содержащей страницы получает WM_COMMAND/EN_CHANGE. Если вы используете это сообщение для включения кнопки ПРИМЕНИТЬ, вам может потребоваться отключить флаг изменения страницы после установки любых списков. Я решил эту проблему, очистив все измененные флаги после установки начального значения. (Я подозреваю, что лист свойств очищает измененный флаг после того, как все сообщения INITDIALOG были отправлены. В любом случае APPLY не включается, если текстовые поля установлены на обработчиках страницы INITDIALOG.) Если я обнаружу ошибку, я выберу этот лист свойств страницы и поместите красный текст в родительский диалог, описывающий ошибку. Единственное, что нужно сделать диалоговым процедурам листа свойств, - это манипулировать их элементами управления.
Одна из вещей, которая озадачивает меня, это как определить, все ли страницы вернули PSNRET_NOERROR из своих сообщений WS_NOTIFY/PSN_APPLY. У меня был некоторый успех при подсчете количества последовательных ответов PSNRET_NOERROR, когда счетчик обнулялся, когда сообщается PSNRET_INVALID или получен PSN_KILLACTIVE. Если количество достигает количества страниц в заголовке листа свойств, я предполагаю, что все страницы вернули PSNRET_NOERROR. Но я беспокоюсь о таких вещах, как отключенные и невидимые страницы.