Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы

© Александр Фролов, Григорий Фролов
Том 17, М.: Диалог-МИФИ, 1994, 287 стр.

[Назад] [Содеожание] [Дальше]

1.2. Инициализация MDI-приложения

В процессе инициализации MDI-приложения вам надо зарегистрировать как минимум два класса окна - класс окна Frame Window (главного окна приложения) и класс окна Document Window.

Регистрация окна Frame Window может выполняться, например, следующим образом:

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);

В классе окна Frame Window, как правило, указывается меню приложения, хотя это меню может быть создано динамически. В поле lpfnWndProc следует записать адрес функции окна Frame Window. Эта функция имеет особенности, о которых мы расскажем позже.

Остальные поля структуры WNDCLASS заполняются обычным образом. Отметим только, что для цвета фона окна имеет смысл использовать константу COLOR_APPWORKSPACE. В этом случае для управления фоном окна можно будет использовать приложение Control Panel, что даст пользователю возможность настраивать цвета по своему вкусу.

Регистрация класса для окна Document Window выполняется, например, так:

memset(&wc, 0, sizeof(wc));
wc.lpszMenuName  = 0;
wc.style         = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc   = (WNDPROC)ChildWndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = sizeof(WORD);
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);

В поле lpfnWndProc вы должны указать адрес функции окна Document Window, которая, как и функция окна Frame Window, имеет свои особенности.

Обратите внимание на поле hIcon. Так как окно Document Window может быть минимизировано пользователем (свернуто в пиктограмму), вы должны определить эту пиктограмму в классе окна Document Window. Если приложение создает окна Document Window на базе нескольких классов и эти окна будут использованы для отображения документов различного типа, для каждого класса имеет смысл определить свою пиктограмму.

Для определения цвета фона окна Document Window мы рекомендуем воспользоваться константой COLOR_WINDOW. При этом пользователь сможет управлять цветом фона окна Document Window при помощи приложения Control Panel.

Заметим, что приложение, которое "ведет себя хорошо", не навязывает пользователю вкусы разработчика приложения, а позволяет ему выполнить настройку внешнего вида самостоятельно. Для того чтобы ваше MDI-приложение было похоже по внешнему виду на стандартные (такие как, например, Program Manager), используйте системные цвета.

Итак, мы зарегистрировали класс для главного окна приложения Frame Window и один или несколько классов для создания окон Document Window. На следующем этапе инициализации нужно создать окна Frame Window и Client Window.

Окно Frame Window создается точно также, как и главное окно обычного приложения. Например:

hwndFrame = CreateWindow(
  szFrameClassName,    // имя класса окна
  szWindowTitle,       // заголовок окна
  WS_OVERLAPPEDWINDOW, // стиль окна
  CW_USEDEFAULT, 0,    // задаем размеры и расположение
  CW_USEDEFAULT, 0,    // окна, принятые по умолчанию
  0,                   // идентификатор родительского окна
  0,                   // идентификатор меню
  hInstance,           // идентификатор приложения
  NULL);               // указатель на дополнительные параметры

Для создания окна Client Window необходимо использовать предопределенный класс окна "MDICLIENT":

CLIENTCREATESTRUCT clcs;
clcs.hWindowMenu = GetSubMenu(GetMenu(hwnd), ID_WINDOWMENU);
clcs.idFirstChild = ID_MDIWINDOW;

hwndClient = CreateWindow(
  "MDICLIENT",    // имя класса окна
  NULL,           // заголовок окна
  WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE | // стиль окна
    WS_HSCROLL | WS_VSCROLL,  
  0, 0, 0, 0,
  hwnd,                   // идентификатор родительского окна
  (HMENU)ID_CLIENTWINDOW, // идентификатор дочернего окна
  hInst,          // идентификатор приложения
  (LPSTR)&clcs);  // указатель на дополнительные параметры

Для окна Client Window не нужно указывать заголовок, зато следует использовать стиль WS_CLIPCHILDREN (так как функция этого окна не будет рисовать поверх окон Document Window). Размеры окна не имеют значения, потому что они автоматически устанавливаются равными размерам внутренней области (client region) окна Frame Window.

Так как окно Client Window является дочерним по отношению к окну Frame Window, в девятом параметре функции CreateWindow необходимо указать идентификатор. Можно использовать произвольное значение, не конфликтующее с идентификаторами других дочерних окон, создаваемых окном Frame Window (например, с идентификаторами окон Toolbar и Statusbar).

Через последний параметр функции CreateWindow следует передать указатель на предварительно проинициализированную структуру CLIENTCREATESTRUCT.

Поле hWindowMenu этой структуры должно содержать идентификатор временного меню "Window", которое определено для любого стандартного MDI-приложения. По мере создания окон Document Window это меню будет дополняться снизу строками, состоящими из заголовков окон Document Window. Эти строки можно использовать для выбора и активизации нужного окна Document Window.

Как получить нужный идентификатор?

Создание окна Client Window целесообразно выполнять в функции окна Frame Window при обработке сообщения WM_CREATE, так как окно Client Window дочернее по отношению к окну Frame Window. В приведенном выше фрагменте кода, взятом как раз из обработчика этого сообщения, вызывается функция GetMenu. В качестве параметра ей передается идентификатор окна Frame Window, поэтому она возвращает идентификатор главного меню приложения.

Далее, пользуясь этим идентификатором и зная порядок расположения временных меню (pop up menu) в главном меню приложения, с помощью функции GetSubMenu можно легко получить идентификатор для любого временного меню. В качестве второго параметра функции GetSubMenu следует передать порядковый номер временного меню. Самому левому временному меню соответствует номер 0, следующему - 1, и т. д. Поэтому идентификатор ID_WINDOWMENU должен быть равен порядковому номеру временного меню "Window" в главном меню приложения.

Теперь о поле idFirstChild.

При создании окон Document Window они получают идентификаторы (как и обычные дочерние окна). Первое созданное окно Document Window получает идентификатор, определенный в поле idFirstChild при создании окна Client Window. Идентификаторы других окон получаются последовательным увеличением значения, заданного в этом поле.

А что произойдет, если одно из окон Document Window будет уничтожено?

В этом случае идентификаторы оставшихся окон Document Window будут изменены таким образом, чтобы они по-прежнему монотонно возрастали начиная со значения idFirstChild.

Основное правило для выбора идентификатора первого окна Document Window состоит в том, что идентификаторы окон Document Window не должны конфликтовать с идентификаторами органов управления и строк меню, создаваемых приложением. Поэтому для инициализации поля idFirstChild следует выбрать такое значение, которое заведомо больше значений других идентификаторов.

[Назад] [Содеожание] [Дальше]