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

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

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