Лист свойств в диалоге с использованием примера WINAPI (без использования MFC)

Может кто-нибудь указать мне на пример WINAPI встраивания листа свойств в диалоговое окно с использованием WINAPI (не MFC)?

2 ответа

Решение

Как насчет примера MSDN.

Для справки, вот некоторые из вещей, которые я узнал, исследуя этот вопрос. Я получил большую часть всего этого, задавая вопросы здесь или выполняя поиск в Интернете. Спасибо всем за помощь!

Я написал класс для инкапсуляции всего, что обнаружил; Если вы заинтересованы в получении копии, пришлите мне письмо по электронной почте 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. Но я беспокоюсь о таких вещах, как отключенные и невидимые страницы.

Другие вопросы по тегам