Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы© Александр Фролов, Григорий ФроловТом 17, М.: Диалог-МИФИ, 1994, 287 стр. 1.8. Приложение MDITBПриложение MDITB отличается от только что рассмотренного нами приложения MDIAPP наличием дополнительных окон Toolbar и Statusbar (рис. 1.11).
Рис. 1.11. Приложение MDITB Для сокращения объема исходного текста мы не стали полностью реализовывать стандартные для окон Toolbar и Statusbar функции, ограничившись демонстрацией способов создания этих окон в MDI-приложении. Один из возможных способов реализации функций окна Toolbar мы описали в 13 томе "Библиотеки системного программиста" (см. разделы "Орган управления TOOLBAR" и "Приложение SMARTPAD" главы "Меню"). Реализация функций окна Statusbar не отнимет у вас много сил, поэтому вы справитесь с этим окном самостоятельно. Итак, обратимся к листингу 1.5, содержащему определения всех функций приложения MDITB. В книге приведен сокращенный вариант листинга (без комментариев), полный вариант вы найдете на дискете, которая продается вместе с книгой. Листинг 1.5. Файл mditb/mditb.cpp // ============================================================ // MDI-приложение с окнами Toolbar и Statusbar // ============================================================ #define STRICT #include <windows.h> #include <mem.h> #include "mditb.hpp" BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export FrameWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK _export ChildWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK _export TbWndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK _export SbWndProc(HWND, UINT, WPARAM, LPARAM); char const szFrameClassName[] = "MDITBAppClass"; char const szChildClassName[] = "MDITBChildAppClass"; char const szTbClassName[] = "MDITBCtrlAppClass"; char const szSbClassName[] = "MDISBCtrlAppClass"; char const szWindowTitle[] = "MDI Application"; HINSTANCE hInst; HWND hwndFrame; // окно Frame Window HWND hwndClient; // окно Client Window HWND hwndChild; // окно Child Window HWND hwndTb; // окно Toolbar HWND hwndSb; // окно Statusbar // ===================================== // Функция WinMain // ===================================== #pragma argsused int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями hInst = hInstance; // сохраняем идентификатор приложения if(hPrevInstance) // может быть запущена return FALSE; // только одна копия приложения if(!InitApp(hInstance)) return FALSE; hwndFrame = CreateWindow( szFrameClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, 0, // задаем размеры и расположение CW_USEDEFAULT, 0, // окна, принятые по умолчанию 0, // идентификатор родительского окна 0, // идентификатор меню hInstance, // идентификатор приложения NULL); // указатель на дополнительные параметры if(!hwndFrame) return FALSE; ShowWindow(hwndFrame, nCmdShow); UpdateWindow(hwndFrame); while(GetMessage(&msg, NULL, 0, 0)) { if(!TranslateMDISysAccel(hwndClient, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; } // ===================================== // Функция InitApp // Выполняет регистрацию класса окна // ===================================== BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации // класса окна memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = "APP_MENU"; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)FrameWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APP_ICON"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1); wc.lpszClassName = (LPSTR)szFrameClassName; aWndClass = RegisterClass(&wc); if(!aWndClass) return FALSE; memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = 0; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)ChildWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APPCLIENT_ICON"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = (LPSTR)szChildClassName; aWndClass = RegisterClass(&wc); if(!aWndClass) return FALSE; // Регистрируем класс для окна Toolbar memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = 0; wc.style = 0; wc.lpfnWndProc = (WNDPROC)TbWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = (LPSTR)szTbClassName; aWndClass = RegisterClass(&wc); if(!aWndClass) return FALSE; // Регистрируем класс для окна Statusbar memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = 0; wc.style = 0; wc.lpfnWndProc = (WNDPROC)SbWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszClassName = (LPSTR)szSbClassName; aWndClass = RegisterClass(&wc); if(!aWndClass) return FALSE; return TRUE; } // ===================================== // Функция FrameWndProc // ===================================== LRESULT CALLBACK _export FrameWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { CLIENTCREATESTRUCT clcs; MDICREATESTRUCT mdics; switch (msg) { // Устанавливаем размеры окон Toolbar, Statusbar. // Уменьшаем размер окна Client Window case WM_SIZE: { // Располагаем окно Toolbar в верхней части // окна Frame Window MoveWindow(hwndTb, 0, // x-координата 0, // y-координата LOWORD(lParam), // ширина TBAR_SIZE, // высота TRUE); // требуется перерисовка окна // Располагаем окно Statusbar в нижней части // окна Frame Window MoveWindow(hwndSb, 0, // x-координата HIWORD(lParam) - SBAR_SIZE, // y-координата LOWORD(lParam), // ширина SBAR_SIZE, // высота TRUE); // требуется перерисовка окна // Если окно не свернуто в пиктограмму и его // идентификатор отличен от нуля, вызываем // функцию MoveWindow if(wParam != SIZEICONIC && hwndClient) { MoveWindow(hwndClient, 0, // x-координата TBAR_SIZE, // y-координата LOWORD(lParam), // ширина HIWORD(lParam) - (TBAR_SIZE + SBAR_SIZE), // высота TRUE); // требуется перерисовка окна // После уменьшения размеров окна нельзя // отдавать сообщение WM_SIZE функции DefFrameProc, // так как иначе размеры будут восстановлены return 0; } break; } case WM_CREATE: { clcs.hWindowMenu = GetSubMenu(GetMenu(hwnd), 1); clcs.idFirstChild = 500; // Создаем окно Client Window hwndClient = CreateWindow( "MDICLIENT", // имя класса окна NULL, // заголовок окна WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | // стиль окна WS_HSCROLL | WS_VSCROLL, 0, 0, 0, 0, hwnd, // идентификатор родительского окна (HMENU)1, // идентификатор меню hInst, // идентификатор приложения (LPSTR)&clcs);// указатель на дополнительные параметры // Создаем окно Toolbar hwndTb = CreateWindow( szTbClassName, // имя класса окна NULL, // заголовок окна WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_DLGFRAME, 0, 0, 0, 0, hwnd, // идентификатор родительского окна (HMENU)2, // идентификатор меню hInst, // идентификатор приложения NULL); // указатель на дополнительные параметры // Создаем окно Statusbar hwndSb = CreateWindow( szSbClassName, // имя класса окна NULL, // заголовок окна WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_DLGFRAME, 0, 0, 0, 0, hwnd, // идентификатор родительского окна (HMENU)3, // идентификатор меню hInst, // идентификатор приложения NULL); // указатель на дополнительные параметры break; } case WM_COMMAND: { switch (wParam) { case CM_FILENEW: { mdics.szClass = szChildClassName; // класс окна mdics.szTitle = "MDI Child Window"; // заголовок окна mdics.hOwner = hInst; // идентификатор приложения mdics.x = CW_USEDEFAULT; // размеры окна mdics.y = CW_USEDEFAULT; // Document Window mdics.cx = CW_USEDEFAULT; mdics.cy = CW_USEDEFAULT; mdics.style = 0; // дополнительные стили mdics.lParam = NULL; // 32-битное значение // Посылаем сообщение WM_MDICREATE окну // Client Window. В результате будет создано новое // окно Document Window hwndChild = (HWND)SendMessage(hwndClient, WM_MDICREATE, 0, (LPARAM)&mdics); break; } case CM_WINDOWTILE: { SendMessage(hwndClient, WM_MDITILE, 0, NULL); break; } case CM_WINDOWCASCADE: { SendMessage(hwndClient, WM_MDICASCADE, 0, NULL); break; } case CM_WINDOWICONS: { SendMessage(hwndClient, WM_MDIICONARRANGE, 0, NULL); break; } case CM_WINDOWCLOSEALL: { HWND hwndTemp; ShowWindow(hwndClient, SW_HIDE); for(;;) { hwndTemp = GetWindow(hwndClient, GW_CHILD); if(!hwndTemp) break; while(hwndTemp && GetWindow(hwndTemp, GW_OWNER)) hwndTemp = GetWindow(hwndTemp, GW_HWNDNEXT); if(hwndTemp) SendMessage(hwndClient, WM_MDIDESTROY, (WPARAM)hwndTemp, NULL); else break; } ShowWindow(hwndClient, SW_SHOW); break; } case CM_HELPABOUT: { MessageBox(hwnd, "Приложение MDIAPP\n(C) Фролов А.В., 1995", "Simple MDI Application", MB_OK | MB_ICONINFORMATION); break; } case CM_FILEEXIT: { DestroyWindow(hwnd); break; } default: break; } HWND hwndChild = (HWND)LOWORD(SendMessage(hwndClient, WM_MDIGETACTIVE, 0, 0l)); if(IsWindow(hwndChild)) SendMessage(hwndChild, WM_COMMAND, wParam, lParam); return DefFrameProc(hwnd, hwndClient, msg, wParam, lParam); } case WM_DESTROY: { PostQuitMessage(0); break; } default: break; } return DefFrameProc(hwnd, hwndClient, msg, wParam, lParam); } // ===================================== // Функция ChildWndProc // ===================================== LRESULT CALLBACK _export ChildWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rc; switch (msg) { case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rc); DrawText(hdc, "Child Window", -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps); } default: break; } return DefMDIChildProc(hwnd, msg, wParam, lParam); } // ===================================== // Функция TbWndProc // ===================================== LRESULT CALLBACK _export TbWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rc; switch (msg) { case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rc); DrawText(hdc, "Toolbar Window", -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps); } default: break; } return DefMDIChildProc(hwnd, msg, wParam, lParam); } // ===================================== // Функция SbWndProc // ===================================== LRESULT CALLBACK _export SbWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps; RECT rc; switch (msg) { case WM_PAINT: { hdc = BeginPaint(hwnd, &ps); GetClientRect(hwnd, &rc); DrawText(hdc, "Statusbar Window", -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER); EndPaint(hwnd, &ps); } default: break; } return DefMDIChildProc(hwnd, msg, wParam, lParam); } На этапе инициализации приложения функция InitApp регистрирует классы для окон Frame Window, Document Window, а также для окон Toolbar и Statusbar. Эта процедура не имеет никаких особенностей. Отметим только, что вы можете изменить форму курсора мыши для окна Toolbar, указав нужный идентификатор курсора в поле hCursor структуры WNDCLASS перед регистрацией класса, или задать цвет окна Toolbar. Окна Toolbar и Statusbar создаются в функции окна Frame Window при обработке сообщения WM_CREATE: hwndTb = CreateWindow( szTbClassName, // имя класса окна NULL, // заголовок окна WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_DLGFRAME, 0, 0, 0, 0, hwnd, // идентификатор родительского окна (HMENU)2, // идентификатор меню hInst, // идентификатор приложения NULL); // указатель на дополнительные параметры hwndSb = CreateWindow( szSbClassName, // имя класса окна NULL, // заголовок окна WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | WS_DLGFRAME, 0, 0, 0, 0, hwnd, // идентификатор родительского окна (HMENU)3, // идентификатор меню hInst, // идентификатор приложения NULL); // указатель на дополнительные параметры Обратите внимание, что для этих окон мы указали нулевые размеры, так как в момент их создания размеры внутренней области окна Frame Window еще неизвестны. Вы можете указать для окон Toolbar и Statusbar любые стили, применимые к дочерним окнам. Для того чтобы эти окна появились на экране сразу после их создания, мы использовали стиль WS_VISIBLE. Займемся теперь обработчиком сообщения WM_SIZE. Прежде всего, он располагает окно Toolbar в верхней части внутренней области окна Frame Window, вызывая функцию MoveWindow: MoveWindow(hwndTb, 0, // x-координата 0, // y-координата LOWORD(lParam), // ширина TBAR_SIZE, // высота TRUE); // требуется перерисовка окна Координаты верхнего левого угла окна Toolbar устанавливаются равными значению (0,0), поэтому верхний левый угол этого окна совмещается с верхним левым углом внутренней области окна Frame Window. Для определения ширины окна Toolbar анализируется параметр lParam, который для сообщения WM_SIZE равен ширине внутренней области окна. Высота окна Toolbar в нашем приложении задается константой TBAR_SIZE, которая определена в файле mditb.hpp (листинг 1.6). Аналогичным образом устанавливаются координаты и размеры окна Statusbar: MoveWindow(hwndSb, 0, // x-координата HIWORD(lParam) - SBAR_SIZE, // y-координата LOWORD(lParam), // ширина SBAR_SIZE, // высота TRUE); // требуется перерисовка окна Высота окна Statusbar задается константой SBAR_SIZE. Затем мы проверяем, создано ли окно Client Window, и не свернуто ли оно в пиктограмму. Если с окном все в порядке, устанавливаем для него новое расположение и размеры, оставляя место для окон Toolbar и Statusbar: if(wParam != SIZEICONIC && hwndClient) { MoveWindow(hwndClient, 0, // x-координата TBAR_SIZE, // y-координата LOWORD(lParam), // ширина HIWORD(lParam) - (TBAR_SIZE + SBAR_SIZE), // высота TRUE); // требуется перерисовка окна return 0; } break; Еще раз обращаем ваше внимание на то, что обработанное сообщение WM_SIZE нельзя отдавать функции DefFrameProc, поэтому после вызова функции MoveWindow мы выполняем возврат из функции окна оператором return. При регистрации классов для окон Toolbar и Statusbar мы указали соответствующие функции окна. Эти функции определены в нашем приложении и называются TbWndProc (для окна Toolbar) и SbWndProc (для окна Statusbar). Эти функции полностью определяют поведение окон Toolbar и Statusbar, выполняя обработку предназначенных для них сообщений. В нашем случае мы просто рисуем в центре этих окон их названия, вызывая функцию DrawText. В листинге 1.6 вы найдете файл mditb.hpp, содержащий определения констант для размеров окон Toolbar и Statusbar, а также для работы с меню. Листинг 1.6. Файл mditb/mditb.hpp #define TBAR_SIZE 35 #define SBAR_SIZE 35 #define CM_HELPABOUT 100 #define CM_FILEEXIT 101 #define CM_FILENEW 102 #define CM_WINDOWTILE 103 #define CM_WINDOWCASCADE 104 #define CM_WINDOWICONS 105 #define CM_WINDOWCLOSEALL 106 Файл ресурсов приложения MDITB приведен в листинге 1.7. Листинг 1.7. Файл mditb/mditb.rc #include "mditb.hpp" APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&New", CM_FILENEW MENUITEM SEPARATOR MENUITEM "E&xit", CM_FILEEXIT END POPUP "&Window" BEGIN MENUITEM "&Tile", CM_WINDOWTILE MENUITEM "&Cascade", CM_WINDOWCASCADE MENUITEM "Arrange &Icons",CM_WINDOWICONS MENUITEM "Close &All", CM_WINDOWCLOSEALL END POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END APP_ICON ICON "mditb.ico" APPCLIENT_ICON ICON "mditbcl.ico" Файл определения модуля приложения MDITB приведен в листинге 1.8. Листинг 1.8. Файл mditb/mditb.def NAME MDITB DESCRIPTION 'Приложение MDITB, (C) 1995, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8120 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple |