Операционная система Windows 95 для программиста© Александр Фролов, Григорий ФроловТом 22, М.: Диалог-МИФИ, 1993, 271 стр. 6.4. Приложение Property Sheet DemoПриложение Property Sheet Demo демонстрирует способ создания простейшего блокнота, который появляется при выборе строки Options из меню File. С помощью блокнота заполняется произвольно выбранная нами структура параметров, приведенная ниже: typedef struct { int nBold; int nItalic; int nUnderline; int nUseTabs; char szKeyWord[80]; } OPTIONS; Первая страница блокнота, которая называется Set Effects, показана на рис. 6.2.
Рис. 6.2. Первая страница блокнота На ней расположены переключатели с независимой фиксацией Bold, Italic и Underline. Если изменить состояние одного из этих переключателей, разблокируется кнопка Apply (которая изначально находится в заблокированном состоянии). На второй странице блокнота с названием Using Tabs (рис. 6.3) находятся два переключателя с зависимой фиксацией Use Tabs и Don't use Tabs.
Рис. 6.3. Вторая страница блокнота Кнопка Apply разблокируется автоматически при выборе страницы Using Tabs, что достигается соответствующей обработкой извещения PSN_SETACTIVE. С помощью страницы Keyword (рис. 6.4) пользователь может ввести некоторое ключевое слово, длина которого не должна превышать 8 символов.
Рис. 6.4. Третья страница блокнота При выборе этой страницы кнопка Apply разблокируется только в том случае, когда ключевое слово было изменено. Если длина нового ключевого слова превышает 8 символов, на экране появляется сообщение, показанное на рис. 6.5.
Рис. 6.5.Сообщение о превышении длины ключевого слова При этом вы не сможете переключиться на другие страницы блокнота до тех пор, пока не будет введено ключевое слово нужной длины. Исходные тексты приложения Property Sheet DemoФункции приложения Property Sheet Demo определены в файле psheet.c (листинг 6.1). Листинг 6.1. Файл psheet\psheet.c #define STRICT #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include "resource.h" #include "afxres.h" #include "psheet.h" // Структура, в которой хранятся параметры typedef struct { int nBold; int nItalic; int nUnderline; int nUseTabs; char szKeyWord[80]; } OPTIONS; OPTIONS opt; // Массив описаний страниц блокнота PROPSHEETPAGE psheetPage[3]; // Заголовок блокнота PROPSHEETHEADER psheetHeader; // Идентификаторы страниц блокнота HPROPSHEETPAGE hPage[3]; HINSTANCE hInst; char szAppName[] = "PropSheetApp"; char szAppTitle[] = "Property Sheet Demo"; // ----------------------------------------------------- // Функция WinMain // ----------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg; hInst = hInstance; // Преверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); return FALSE; } // Регистрируем класс окна memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0); wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU); wc.lpszClassName = szAppName; if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE; // Создаем главное окно приложения hWnd = CreateWindow(szAppName, szAppTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE); // Отображаем окно и запускаем цикл обработки сообщений ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage (&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ----------------------------------------------------- // Функция WndProc // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_CREATE, WndProc_OnCreate); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); HANDLE_MSG(hWnd, WM_COMMAND, WndProc_OnCommand); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } } // ----------------------------------------------------- // Функция WndProc_OnCreate // ----------------------------------------------------- BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct) { // Инициализируем библиотеку стандартных органов управления InitCommonControls(); // Инициализируем параметры opt.nBold = opt.nItalic = opt.nUnderline = opt.nUseTabs = 0; strcpy(opt.szKeyWord, "Undef"); return TRUE; } // ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { PostQuitMessage(0); return 0L; } // ----------------------------------------------------- // Функция WndProc_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case ID_FILE_EXIT: PostQuitMessage(0); // завершаем работу приложения return 0L; break; case ID_HELP_ABOUT: MessageBox(hWnd, "Property Sheet Demo Application, v.1.0\n" "(C) Alexandr Frolov, 1995\n" "Email: frolov@glas.apc.org", szAppTitle, MB_OK | MB_ICONINFORMATION); return 0L; break; case ID_FILE_OPTIONS: { // Инициализируем страницы блокнота 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; // Добавляем страницу в блокнот, сохраняя ее // идентификатор в массиве hPage hPage[0] = CreatePropertySheetPage(&psheetPage[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; hPage[1] = CreatePropertySheetPage(&psheetPage[1]); 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; hPage[2] = CreatePropertySheetPage(&psheetPage[2]); // Инициализируем заголовок блокнота 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]; // Создаем и отображаем блокнот PropertySheet(&psheetHeader); return 0L; break; } default: break; } return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify, DefWindowProc); } // ----------------------------------------------------- // Функция DlgProc1 // для первой страницы блокнота // ----------------------------------------------------- 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; } // ----------------------------------------------------- // Функция DlgProc1_OnInitDialog // Вызывается при инициализации первой страницы блокнота // ----------------------------------------------------- BOOL DlgProc1_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam) { // Устанавливаем переключатели в соответствии // со значениями параметров, записанных в // структуре opt SendMessage(GetDlgItem(hdlg, IDC_BOLD), BM_SETCHECK, opt.nBold, 0L); SendMessage(GetDlgItem(hdlg, IDC_ITALIC), BM_SETCHECK, opt.nItalic, 0L); SendMessage(GetDlgItem(hdlg, IDC_UNDERLINE), BM_SETCHECK, opt.nUnderline, 0L); return TRUE; } // ----------------------------------------------------- // Функция DlgProc1_OnNotify // Обрабатывает извещение от первой страницы блокнота // ----------------------------------------------------- LRESULT DlgProc1_OnNotify(HWND hdlg, int idFrom, NMHDR* pnmhdr) { switch(pnmhdr->code) { // Когда пользователь нажимает кнопку OK, получаем // состояние переключателей, расположенных в первой // странице блокнота, и сохраняем это состояние в // соответствующих полях структуры opt case PSN_APPLY: { opt.nBold = (int)SendMessage( GetDlgItem(hdlg, IDC_BOLD), BM_GETCHECK, 0L, 0L); opt.nItalic = (int)SendMessage( GetDlgItem(hdlg, IDC_ITALIC), BM_GETCHECK, 0L, 0L); opt.nUnderline = (int)SendMessage( GetDlgItem(hdlg, IDC_UNDERLINE), BM_GETCHECK, 0L, 0L); break; } default: break; } return FALSE; } // ----------------------------------------------------- // Функция DlgProc1_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void DlgProc1_OnCommand(HWND hdlg, int id, HWND hwndCtl, UINT codeNotify) { // Разблокируем кнопку "Apply" PropSheet_Changed(GetParent(hdlg), hdlg); return NULL; } // ----------------------------------------------------- // Функция DlgProc2 // для второй страницы блокнота // ----------------------------------------------------- BOOL APIENTRY DlgProc2(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc2_OnInitDialog); HANDLE_MSG(hdlg, WM_NOTIFY, DlgProc2_OnNotify); default: break; } return FALSE; } // ----------------------------------------------------- // Функция DlgProc2_OnInitDialog // Вызывается при инициализации второй страницы блокнота // ----------------------------------------------------- BOOL DlgProc2_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam) { SendMessage(GetDlgItem(hdlg, IDC_USETABS), BM_SETCHECK, opt.nUseTabs, 0L); SendMessage(GetDlgItem(hdlg, IDC_DONTUSETABS), BM_SETCHECK, (opt.nUseTabs) ? 0 : 1, 0L); return TRUE; } // ----------------------------------------------------- // Функция DlgProc2_OnNotify // Обрабатывает извещение от второй страницы блокнота // ----------------------------------------------------- LRESULT DlgProc2_OnNotify(HWND hdlg, int idFrom, NMHDR* pnmhdr) { switch(pnmhdr->code) { case PSN_SETACTIVE: { // Разблокируем кнопку Apply, когда страница // блокнота становится активной PropSheet_Changed(GetParent(hdlg), hdlg); break; } case PSN_KILLACTIVE: { // Блокируем кнопку Apply, когда страница // блокнота становится неактивной PropSheet_UnChanged(GetParent(hdlg), hdlg); break; } case PSN_APPLY: { opt.nUseTabs = (int)SendMessage( GetDlgItem(hdlg, IDC_USETABS), BM_GETCHECK, 0L, 0L); break; } default: break; } return FALSE; } // ----------------------------------------------------- // Функция DlgProc3 // для третьей страницы блокнота // ----------------------------------------------------- BOOL APIENTRY DlgProc3(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc3_OnInitDialog); HANDLE_MSG(hdlg, WM_NOTIFY, DlgProc3_OnNotify); HANDLE_MSG(hdlg, WM_COMMAND, DlgProc3_OnCommand); default: break; } return FALSE; } // ----------------------------------------------------- // Функция DlgProc3_OnInitDialog // Вызывается при инициализации третьей страницы блокнота // ----------------------------------------------------- BOOL DlgProc3_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam) { SetWindowText(GetDlgItem(hdlg, IDC_EDITKEYWORD), opt.szKeyWord); return TRUE; } // ----------------------------------------------------- // Функция DlgProc3_OnNotify // Обрабатывает извещение от третьей страницы блокнота // ----------------------------------------------------- LRESULT DlgProc3_OnNotify(HWND hdlg, int idFrom, NMHDR* pnmhdr) { switch(pnmhdr->code) { // Выполняем проверку длины ключевого слова, // которое не должно быть длиннее 8 символов case PSN_KILLACTIVE: { char szTempBuf[80]; // Получаем новое ключевое слово во временный буфер GetWindowText(GetDlgItem(hdlg, IDC_EDITKEYWORD), szTempBuf, 80); // Проверяем его длину if(lstrlen(szTempBuf) > 8) { // Если длина больше 8 символов, выводим // предупреждающее сообщение MessageBox(NULL, "Too long keyword, " "must be shorter than 8 characters", szAppName, MB_OK | MB_ICONEXCLAMATION); // Отменяем закрытие страницы блокнота SetWindowLong(hdlg, DWL_MSGRESULT, TRUE); // Отменяем обновление параметров return TRUE; } else { // Если длина сообщения правильная, разрешаем // закрытие страницы блокнота и // обновление параметров SetWindowLong(hdlg, DWL_MSGRESULT, FALSE); return FALSE; } break; } case PSN_APPLY: { GetWindowText(GetDlgItem(hdlg, IDC_EDITKEYWORD), opt.szKeyWord, 80); break; } default: break; } return FALSE; } // ----------------------------------------------------- // Функция DlgProc3_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void DlgProc3_OnCommand(HWND hdlg, int id, HWND hwndCtl, UINT codeNotify) { // Сообщение от редактора текста if(id == IDC_EDITKEYWORD) { // Если пользователь изменил ключевое слово в окне // редактирования, разблокируем кнопку "Apply" if(codeNotify == EN_CHANGE) { PropSheet_Changed(GetParent(hdlg), hdlg); } } return FALSE; } В файле psheet.h (листинг 6.2) находятся описания функций, определенных в приложении Property Sheet Demo. Листинг 6.2. Файл psheet\psheet.h // ----------------------------------------------------- // Описание функций // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct); void WndProc_OnDestroy(HWND hWnd); void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify); LRESULT WndProc_OnNotify(HWND hWnd, int idFrom, NMHDR FAR * pnmhdr); BOOL APIENTRY DlgProc1(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam); BOOL DlgProc1_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); LRESULT DlgProc1_OnNotify(HWND hWnd, int idFrom, NMHDR* pnmhdr); void DlgProc1_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify); BOOL APIENTRY DlgProc2(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam); BOOL DlgProc2_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); LRESULT DlgProc2_OnNotify(HWND hWnd, int idFrom, NMHDR* pnmhdr); BOOL APIENTRY DlgProc3(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam); BOOL DlgProc3_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); LRESULT DlgProc3_OnNotify(HWND hWnd, int idFrom, NMHDR* pnmhdr); void DlgProc3_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify); В файле resource.h (создается автоматически системой Microsoft Visual C++) находятся определения констант для работы с ресурсами приложения Property Sheet Demo. Этот файл представлен в листинге 6.3. Листинг 6.3. Файл psheet\resource.h //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by PSHEET.RC // #define IDR_APPMENU 102 #define IDI_APPICON 103 #define IDI_APPICONSM 104 #define IDI_EFFECTS 105 #define IDI_TAB 106 #define IDI_KEYWORD 107 #define IDD_DIALOG1 121 #define IDD_DIALOG2 122 #define IDD_DIALOG3 123 #define IDC_BOLD 1000 #define IDC_ITALIC 1001 #define IDC_UNDERLINE 1002 #define IDC_USETABS 1004 #define IDC_EDITKEYWORD 1006 #define IDC_DONTUSETABS 1007 #define ID_FILE_EXIT 40001 #define ID_HELP_ABOUT 40003 #define ID_FILE_OPTIONS 40029 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 125 #define _APS_NEXT_COMMAND_VALUE 40030 #define _APS_NEXT_CONTROL_VALUE 1008 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif Файл psheet.rc (листинг 6.4) содержит определение ресурсов приложения Property Sheet Demo. Он создается автоматически. Листинг 6.4. Файл psheet\psheet.rc //Microsoft Visual C++ generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Menu // IDR_APPMENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&Options...", ID_FILE_OPTIONS MENUITEM SEPARATOR MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Help" BEGIN MENUITEM "&About...", ID_HELP_ABOUT END END #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END ////////////////////////////////////////////////////////////// #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Icon // IDI_APPICON ICON DISCARDABLE "psheet.ico" IDI_APPICONSM ICON DISCARDABLE "psheetsm.ico" IDI_EFFECTS ICON DISCARDABLE "EFFECTS.ICO" IDI_TAB ICON DISCARDABLE "TAB.ICO" IDI_KEYWORD ICON DISCARDABLE "KEYWORD.ICO" ////////////////////////////////////////////////////////////// // Dialog // IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 186, 95 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU FONT 8, "MS Sans Serif" BEGIN CONTROL "Bold",IDC_BOLD,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,17,23,35,10 CONTROL "Italic",IDC_ITALIC,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,17,34,35,10 CONTROL "Underline",IDC_UNDERLINE,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,17,45,46,10 GROUPBOX "Effects",IDC_STATIC,6,9,112,53 END IDD_DIALOG2 DIALOG DISCARDABLE 0, 0, 186, 95 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU FONT 8, "MS Sans Serif" BEGIN CONTROL "Use Tabs", IDC_USETABS, "Button", BS_AUTORADIOBUTTON,18,22, 63,10 GROUPBOX "Tabs",IDC_STATIC,9,7,105,63 CONTROL "Don't use Tabs",IDC_DONTUSETABS,"Button", BS_AUTORADIOBUTTON,18,35,83,10 END IDD_DIALOG3 DIALOG DISCARDABLE 0, 0, 211, 102 STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU FONT 8, "MS Sans Serif" BEGIN EDITTEXT IDC_EDITKEYWORD,22,35,138,13,ES_AUTOHSCROLL GROUPBOX "Keyword",IDC_STATIC,9,15,166,52 END ////////////////////////////////////////////////////////////// // String Table // STRINGTABLE DISCARDABLE BEGIN ID_FILE_EXIT "Quits the application" END #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // Generated from the TEXTINCLUDE 3 resource. ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED Описание функцийЗаймемся описанием глобальных переменных и функций приложения Property Sheet Demo. Глобальные переменныеСтруктура opt, имеющая тип OPTIONS, содержит параметры, которые настраиваются с помощью блокнота. Тип OPTIONS определен в нашем приложении. Массив структур psheetPage типа PROPSHEETPAGE хранит описания страниц блокнота. Заголовок блокнота записан в структуре psheetHeader типа PROPSHEETHEADER. Идентификаторы отдельных страниц, создаваемых функцией CreatePropertySheetPage, записываются в массив структур hPage типа HPROPSHEETPAGE. В переменной hInst хранится идентификатор приложения, полученный функцией WinMain. Строчные массивы szAppName и szAppTitle хранят, соответственно, имя и заголовок приложения. WinMainФункция WinMain не имеет никаких особенностей, поэтому для экономии места в книге мы не будем ее описывать. WndProcФункция WndProc обрабатывает следующие сообщения: WM_CREATE, WM_DESTROY и WM_COMMAND. Обработка выполняется с использованием макрокоманды HANDLE_MSG. WndProc_OnCreateОбработчик сообщения WM_CREATE инициализирует библиотеку стандартных органов управления, вызывая для этого функцию InitCommonControls. Затем он выполняет начальную инициализацию полей структуры параметров opt. WndProc_OnDestroyФункция WndProc_OnDestroy вызывается, когда пользователь завершает работу приложения. Она останавливает цикл обработки сообщений, вызывая функцию PostQuitMessage. WndProc_OnCommandОбработчики сообщений от строк Exit и About действуют также, как и в предыдущем приложении. Когда пользователь выбирает из меню File строку Options, соответствующий обработчик создает блокнот и отображает его на экране. Процедура создания и отображения блокнота заключается в заполнении массива структур psheetPage, содержащих описания страниц, массива psheetHeader с описанием заголовка блокнота. Каждая страница добавляется в блокнот отдельно функцией CreatePropertySheetPage, причем идентификатор созданное страницы записывается в соответствующий элемент массива hPage. Блокнот создается и отображается функцией PropertySheet, которой в качестве единственного параметра передается адрес заполненной структуры заголовка блокнота. DlgProc1Функция DlgProc1 - это функция диалога для первой страницы блокнота. Она обрабатывает сообщения WM_INITDIALOG, WM_COMMAND и WM_NOTIFY, вызывая для них, соответственно, функции DlgProc1_OnInitDialog, DlgProc1_OnCommand и DlgProc1_OnNotify. Заметим, что ни сама функция диалога, ни одна из только что перечисленных функций обработки сообщений не вызывает функцию EndDialog, так как в противном случае блокнот будет уничтожен. DlgProc1_OnInitDialogФункция DlgProc1_OnInitDialog обрабатывает сообщение WM_INITDIALOG, которое поступает в функцию диалога первой страницы блокнота при инициализации последней. Единственное, что она делает, это устанавливает переключатели, расположенные на первой странице, в соответствии с содержимым полей структуры параметров opt: SendMessage(GetDlgItem (hdlg, IDC_BOLD), BM_SETCHECK, opt.nBold, 0L); Метод установки состояния переключателей в диалоговой панели основан на использовании сообщения BM_SETCHECK. Это сообщение, а также функция GetDlgItem, возвращающая идентификатор окна органа управления, расположенного в диалоговой панели, были подробно описаны в 12 томе "Библиотеки системного программиста". DlgProc1_OnNotifyФункция DlgProc1_OnNotify обрабатывает извещения, которые поступают в функцию диалога первой страницы в форме сообщения WM_NOTIFY. В ответ на извещение PSN_APPLY функция DlgProc1_OnNotify получает состояние переключателей и записывает его в соответствующие поля структуры opt: opt.nBold = (int)SendMessage(GetDlgItem(hdlg, IDC_BOLD), BM_GETCHECK, 0L, 0L); Для определения состояния переключателей им посылается сообщение BM_GETCHECK. DlgProc1_OnCommandФункция DlgProc1_OnCommand обрабатывает сообщение WM_COMMAND, поступающее от переключателей, расположенных на первой странице. Как только пользователь нажмет какую-либо из имеющихся на первой странице блокнота кнопку, функция DlgProc1_OnCommand разблокирует кнопку Apply, посылая блокноту сообщение PSM_CHANGED: PropSheet_Changed(GetParent(hdlg), hdlg); Сообщение посылается макрокомандой PropSheet_Changed. В качестве первого параметра этой макрокоманды (как и других макрокоманд, предназначенных для посылки сообщений блокноту) необходимо указать идентификатор окна блокнота. Окно блокнота является родительским по отношению к окнам страниц, поэтому мы воспользовались функцией GetParent . В качестве второго параметра необходимо указать идентификатор диалога, который передается в функцию диалога. DlgProc2Функция DlgProc2 является функцией диалога для второй страницы блокнота. Она обрабатывает сообщения WM_INITDIALOG и WM_NOTIFY, вызывая для них, соответственно, функции DlgProc2_OnInitDialog и DlgProc2_OnNotify. DlgProc2_OnInitDialogФункция DlgProc2_OnInitDialog обрабатывает сообщение WM_INITDIALOG, которое поступает в функцию диалога второй страницы блокнота при ее инициализации. Ее задача аналогична задаче функции DlgProc1_OnInitDialog - установка переключателей, расположенных на второй странице в соответствии с содержимым полей структуры параметров opt. Переключатель с идентификатором IDC_DONTUSETABS всегда устанавливается в состояние, противоположное состоянию переключателя IDC_USETABS. DlgProc2_OnNotifyФункция DlgProc2_OnNotify обрабатывает извещения, которые поступают в функцию диалога второй страницы. Когда пользователь переключается на вторую страницу, делая ее активной, функция диалога получает извещение PSN_SETACTIVE. Обработчик этого извещения разблокирует кнопку Apply, вызывая для этого макрокоманду PropSheet_Changed. Извещение PSN_KILLACTIVE поступает в функцию диалога, когда пользователь завершил работу с текущей страницей блокнота и пытается переключиться на другую страницу. При этом кнопка Apply блокируется при помощи макрокоманды PropSheet_UnChanged : PropSheet_UnChanged(GetParent(hdlg), hdlg); В ответ на извещение PSN_APPLY функция DlgProc2_OnNotify получает состояние переключателя IDC_USETABS и записывает его в поле nUseTabs структуры opt. DlgProc3Функция DlgProc3 является функцией диалога третьей страницы блокнота. Она обрабатывает сообщения WM_INITDIALOG, WM_COMMAND и WM_NOTIFY, вызывая для них, соответственно, функции DlgProc3_OnInitDialog, DlgProc3_OnCommand и DlgProc3_OnNotify. DlgProc3_OnInitDialogФункция DlgProc3_OnInitDialog устанавливает содержимое текстового редактора IDC_EDITKEYWORD, записывая в него строку из поля opt.szKeyWord. DlgProc3_OnNotifyФункция DlgProc3_OnNotify обрабатывает извещения, которые поступают в функцию диалога второй страницы. Если пользователь изменил текст в окне редактора IDC_EDITKEYWORD, обработчик сообщения WM_COMMAND (будет описан ниже) разблокирует кнопку Apply. Если теперь пользователь попытается нажать эту кнопку или кнопку OK, либо попытается переключиться на другую страницу блокнота, функция диалога третьей страницы получит извещение PSN_KILLACTIVE. Обработчик этого извещения записывает содержимое текстового редактора во временный буфер szTempBuf и с помощью функции lstrlen определяет его длину. Если длина больше 8 символов, на экран выводится предупреждающее сообщение, после чего выполняется блокирование страницы. Теперь пользователь будет вынужден ввести ключевое слово правильной длины, иначе он не сможет переключиться на другую страницу или завершить работу с блокнотом. Для блокирования обработчик извещения PSN_KILLACTIVE возвращает значение TRUE, установив предварительно код завершения TRUE в структуре окна диалога с помощью функции SetWindowLong : SetWindowLong(hdlg, DWL_MSGRESULT, TRUE); return TRUE; Если же длина полученной текстовой строки меньше 8 символов, обработчик извещения PSN_KILLACTIVE возвращает значение FALSE: SetWindowLong(hdlg, DWL_MSGRESULT, FALSE); return FALSE; В результате функция диалога третьей и остальных страниц получает извещение PSN_APPLY. При этом обработчик извещения PSN_APPLY третьей страницы сохраняет новое ключевое слово в поле opt.szKeyWord. DlgProc3_OnCommandКнопка Apply, расположенная на третьей странице блокнота, разблокируется только в том случае, когда пользователь изменил ключевое слово. Для этого обработчик сообщения WM_COMMAND, реализованный функцией DlgProc3_OnCommand, отслеживает извещение EN_CHANGE. Это извещение поступает от окна текстового редактора в форме сообщения WM_COMMAND в том случае, когда пользователь изменил редактируемый текст (см. том 12 "Библиотеки системного программиста"). Разблокировка кнопки Apply выполняется при помощи макрокоманды PropSheet_Changed. |