Операционная система 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 |

