Операционная система Windows 95 для программиста© Александр Фролов, Григорий ФроловТом 22, М.: Диалог-МИФИ, 1993, 271 стр. 6.1. Создание органа управления Property SheetОрган управления Property Sheet - это системная немодальная диалоговая панель, состоящая из нескольких страниц. Перед тем как продолжить чтение, мы рекомендуем вам обратиться к главе "Диалоговые панели", расположенной в 12 томе "Библиотеки системного программиста", и вспомнить, как работают обычные диалоговые панели. Для создания органа управления Property Sheet вы должны сделать следующее:
Сразу отметим, что для создания органа управления Wizard необходимо использовать точно такую же процедуру. Отличия заключаются в заполнении структур и обработке сообщений в функциях диалога. Рассмотрим перечисленные выше шаги подробнее. Подготовка шаблонов диалоговых панелейШаблон диалоговой панели готовится обычным способом с помощью интегрированной среды разработки приложений Microsoft Visual C++. Единственное, на что нам хотелось бы обратить внимание: на страницах блокнота и органа управления Wizard нет кнопок OK, Cancel и Help, так как эти кнопки относятся ко всему блокноту и располагаются в нижней части его окна. В остальном шаблоны страниц блокнота ничем не отличаются от шаблонов обычных диалоговых панелей. Функция диалога страницы блокнотаФункция диалога страницы блокнота обычно обрабатывает сообщения WM_INITDIALOG , WM_COMMAND и WM_NOTIFY . Первое из них передается функции диалога при его инициализации, второе поступает от органов управления, расположенных на странице блокнота. Сообщение WM_NOTIFY генерируется в том случае, когда пользователь нажимает кнопки OK, Cancel, Help, Apply (а также кнопки Back, Next и Finish в органе управления Wizard) или переходит к просмотру другой страницы блокнота. Ниже мы рассмотрим эти сообщения подробнее. В отличие от обычной функции диалога, функция диалога для страницы блокнота или органа управления Wizard не вызывает функцию EndDialog , так как это привело бы к уничтожению органа управления. Вот пример функции диалога для страницы блокнота, взятый из приложения Property Sheet Demo, полные исходные тексты которого вы найдете ниже в разделе "Приложение Property Sheet Demo": BOOL APIENTRY DlgProc1(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc1_OnInitDialog); HANDLE_MSG(hdlg, WM_COMMAND, DlgProc1_OnCommand); HANDLE_MSG(hdlg, WM_NOTIFY, DlgProc1_OnNotify); default: break; } return FALSE; } Так же как и в функции главного окна приложений, в функции диалога для организации обработки сообщений мы воспользовались удобной макрокомандой HANDLE_MSG . Подготовка массива структур PROPSHEETPAGEДля каждой страницы блокнота или органа управления Wizard вы должны подготовить структуру PROPSHEETPAGE , записав в нее описание страницы. Структура PROPSHEETPAGE определена следующим образом: typedef struct _PROPSHEETPAGE { DWORD dwSize; // размер структуры DWORD dwFlags; // флаги HINSTANCE hInstance; // идентификатор приложения union { LPCTSTR pszTemplate; // идентификатор ресурса // диалоговой панели LPCDLGTEMPLATE pResource; // шаблон диалоговой панели }; union { HICON hIcon; // идентификатор пиктограммы LPCTSTR pszIcon; // идентификатор ресурса пиктограммы }; LPCTSTR pszTitle; // заголовок страницы DLGPROC pfnDlgProc; // адрес функции диалога LPARAM lParam; // дополнительный параметр LPFNPSPCALLBACK pfnCallback;// адрес функции обратного вызова UINT FAR * pcRefParent;// указатель на счетчик использования } PROPSHEETPAGE, FAR *LPPROPSHEETPAGE; typedef const PROPSHEETPAGE FAR *LPCPROPSHEETPAGE; Возможно, эта структура выглядит несколько громоздко, однако ее заполнение не вызовет у вас никаких затруднений. В поле dwSize необходимо записать размер структуры, т. е. значение sizeof(PROPSHEETPAGE). В поле dwFlags следует записать флаги, отмечающие
задействованные поля структуры и определяющие
внешний вид органа управления. Здесь можно
использовать следующие значения:
В нашем приложении мы использовали флаги PSP_USETITLE и PSP_USEICONID. Поле hInstance должно содержать идентификатор приложения, который передается через соответствующий параметр функции WinMain. Поля pszTemplate и pResource объединены, поэтому вы можете использовать только одно из них. По умолчанию вы должны записать в поле pszTemplate идентификатор ресурса, содержащего шаблон диалоговой панели. Однако указав флаг PSP_DLGINDIRECT, вы вместо этого можете создать шаблон диалоговой панели динамически в оперативной памяти и записать указатель на этот шаблон в поле pResource. Аналогичным образом объединены поля hIcon и pszIcon. Если в поле pszIcon вы собираетесь записать идентификатор пиктограммы, которая будет отображаться в закладке, следует указать флаг PSP_USEHICON. Если же пиктограмма определена в ресурсах приложения, вы должны записать соответствующий идентификатор ресурса в поле pszIcon и указать флаг PSP_USEICONID. В том случае, когда вам не нужно отображать пиктограмму на закладке, не указывайте флаги PSP_USEHICON и PSP_USEICONID, а в поле pszIcon (или hIcon, что одно и то же) запишите значение NULL. При создании шаблона диалоговой панели вы можете указать ее заголовок. Однако вместо этого можно использовать поле pszTitle и флаг PSP_USEICONID. В поле pfnDlgProc необходимо записать адрес функции диалога, подготовленной с учетом приведенных выше замечаний. Вот как мы заполнили поля структуры PROPSHEETPAGE для трех страниц блокнота в приложении Property Sheet Demo: PROPSHEETPAGE psheetPage[3]; psheetPage[0].dwSize = sizeof(PROPSHEETPAGE); psheetPage[0].hInstance = hInst; psheetPage[0].pszIcon = MAKEINTRESOURCE(IDI_EFFECTS); psheetPage[0].dwFlags = PSP_USETITLE | PSP_USEICONID; psheetPage[0].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG1); psheetPage[0].pfnDlgProc = DlgProc1; psheetPage[0].pszTitle = "Set Effects"; psheetPage[0].lParam = 0; psheetPage[1].dwSize = sizeof(PROPSHEETPAGE); psheetPage[1].hInstance = hInst; psheetPage[1].pszIcon = MAKEINTRESOURCE(IDI_TAB); psheetPage[1].dwFlags = PSP_USETITLE | PSP_USEICONID; psheetPage[1].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG2); psheetPage[1].pfnDlgProc = DlgProc2; psheetPage[1].pszTitle = "Using Tabs"; psheetPage[1].lParam = 0; psheetPage[2].dwSize = sizeof(PROPSHEETPAGE); psheetPage[2].hInstance = hInst; psheetPage[2].pszIcon = MAKEINTRESOURCE(IDI_KEYWORD); psheetPage[2].dwFlags = PSP_USETITLE | PSP_USEICONID; psheetPage[2].pszTemplate = MAKEINTRESOURCE(IDD_DIALOG3); psheetPage[2].pfnDlgProc = DlgProc3; psheetPage[2].pszTitle = "Keyword"; psheetPage[2].lParam = 0; Заполнение структуры PROPSHEETHEADER и создание блокнотаСтруктура PROPSHEETHEADER , как это видно из ее названия, описывает заголовок блокнота. Она имеет следующий формат: typedef struct _PROPSHEETHEADER { DWORD dwSize; // размер структуры DWORD dwFlags; // флаги HWND hwndParent; // идентификатор родительского окна HINSTANCE hInstance; // идентификатор приложения union { HICON hIcon; // идентификатор пиктограммы LPCTSTR pszIcon; // идентификатор ресурса пиктограммы }; LPCTSTR pszCaption; // заголовок блокнота UINT nPages; // количество страниц в блокноте union { UINT nStartPage; // номер первой страницы LPCTSTR pStartPage; // имя первой страницы }; union { LPCPROPSHEETPAGE ppsp; // адрес массива структур // PROPSHEETPAGE HPROPSHEETPAGE FAR *phpage; // адрес массива }; // идентификаторов страниц блокнота PFNPROPSHEETCALLBACK pfnCallback; // адрес функции // обратного вызова } PROPSHEETHEADER, FAR *LPPROPSHEETHEADER; typedef const PROPSHEETHEADER FAR *LPCPROPSHEETHEADER; В поле dwSize нужно записать размер структуры. Поле dwFlags может содержать логическую
комбинацию следующих значений:
В приложении Property Sheet Demo мы заполнили структуру PROPSHEETHEADER следующим образом: PROPSHEETHEADER psheetHeader; psheetHeader.dwSize = sizeof(PROPSHEETHEADER); psheetHeader.hInstance = hInst; psheetHeader.pszIcon = MAKEINTRESOURCE(IDI_APPICONSM); psheetHeader.dwFlags = PSH_USEICONID; psheetHeader.hwndParent = hWnd; psheetHeader.pszCaption = "Property Sheet Sample"; psheetHeader.nPages = sizeof(psheetPage) / sizeof(PROPSHEETPAGE); psheetHeader.phpage = (HPROPSHEETPAGE FAR *)&hPage[0]; Обратите внимание на поле phpage. В него мы записали адрес массива идентификаторов страниц HPROPSHEETPAGE. Этот массив заполняется функцией CreatePropertySheetPage, которой в качестве параметра передается адрес предварительно заполненной структуры PROPSHEETPAGE: HPROPSHEETPAGE hPage[3]; hPage[0] = CreatePropertySheetPage(&psheetPage[0]); hPage[1] = CreatePropertySheetPage(&psheetPage[1]); hPage[2] = CreatePropertySheetPage(&psheetPage[2]); После заполнения заголовка блокнота можно создать блокнот (или орган управления Wizard, если указан флаг PSH_WIZARD ) при помощи функции PropertySheet : PropertySheet(&psheetHeader); Есть еще один способ создания блокнота или органа управления Wizard без использования функции CreatePropertySheetPage. Мы применили этот способ в приложении Wizard Demo: psheetHeader.dwSize = sizeof(PROPSHEETHEADER); psheetHeader.hInstance = hInst; psheetHeader.pszIcon = NULL; psheetHeader.dwFlags = PSH_PROPSHEETPAGE | PSH_WIZARD; psheetHeader.hwndParent = hWnd; psheetHeader.pszCaption = "Property Sheet Sample"; psheetHeader.nPages = sizeof(psheetPage) / sizeof(PROPSHEETPAGE); psheetHeader.ppsp = (LPCPROPSHEETPAGE)&psheetPage; PropertySheet(&psheetHeader); Здесь мы не используем пиктограмму, так как она все равно не отображается, поэтому в поле pszIcon записано значение NULL. Кроме того, указаны флаги PSH_PROPSHEETPAGE и PSH_WIZARD. Так как указан флаг PSH_PROPSHEETPAGE, то в поле ppsp мы записываем адрес массива структур PROPSHEETPAGE, описывающих отдельные страницы органа управления Wizard. При этом нам не нужен массив идентификаторов страниц и, соответственно, функция CreatePropertySheetPage, которая его заполняет. |