Шаг 48.
Библиотека OWL.
Регистрация нового класса окна

    На этом шаге мы рассмотрим создание дочернего окна с характеристиками, заданными пользователем.

    Когда вы создаете новое окно, происходящее из TWindow (или из класса, происходящего из TWindow), OWL автоматически регистрирует класс окна Windows. Класс окна в Windows не следует путать с классом окна в OWL. Windows использует оконные классы, чтобы различать типы окон. Этот тип оконного класса включает такие атрибуты окна, как пиктограмму окна, меню, курсор и цвет фона. После регистрации оконного класса в Windows вы можете создавать столько окон, сколько вам необходимо, но это будут окна одного класса. Вы можете рассматривать класс окна для Windows как ксерокопию, описывающую тип окна.

    Если вы не изменили Windows-класс своих OWL-окон, OWL задаст этот класс по умолчанию как Windows-класс. Характерные черты таких окон: цвет фона - белый, курсор в виде стрелки, имя класса по умолчанию и возможность реагировать на двойное нажатие кнопки мыши. До тех пор, пока этот задаваемый по умолчанию класс окна удовлетворяет вашим требованиям, вы можете не беспокоиться о регистрации класса окна. Но если вы захотели иметь окно с серым фоном, другим курсором или какими-то особыми атрибутами? В этом случае вы должны зарегистрировать свой собственный класс окна для Windows. Чтобы сделать это, вам необходимо:


    Замечание. Чтобы понять, как регистрировать собственные оконные классы, необходимо понять разницу между классом окна в OWL и классом окна в Windows. Первый из них включает в себя функции и данные, управляющие объектом окна. Например, TWindow - это OWL-класс окна. Класс окна для Windows - это множество атрибутов, которые описывают тип создаваемого вами окна. Как только окно создано, его копия хранится в памяти и может быть использована для создания множества окон этого класса в Windows. Класс окна Windows аналогичен стилю окна, но он еще определяет атрибуты, которые не изменяются во время выполнения приложения. Класс окна Windows почти наверняка имеет имя типа ChildWundow или DrawingWindow.

    Создание нового класса окна под Windows дает вам возможность сделать ваши приложения идеально настраиваемыми, разработав огромное число уникальных типов окон. Чтобы увидеть, как это все делается, запустите программу, приведенную в следующем примере.


    Приложение показывает, как зарегистрировать новый оконный класс. Вы можете создавать окна, которые не основываются на задаваемых по умолчанию атрибутах в OWL-классе.
#include <owl\applicat.h>
#include <owl\framewin.h>
#include "pr48_1.rc"

//Класс приложения.
class TApp: public TApplication
{
  public:
	 TApp():TApplication() {}
	 void InitMainWindow();
};

// Класс главного окна.
class TWndw : public  TFrameWindow
{
  public:
    TWndw (TWindow *parent, const char far *title);
  protected:
    int  childX, childY;
    void CmOpen(); 
    void CmClose();

    DECLARE_RESPONSE_TABLE (TWndw);
};

DEFINE_RESPONSE_TABLE1 (TWndw, TFrameWindow)
	EV_COMMAND (CM_OPEN, CmOpen),
	EV_COMMAND (CM_CLOSE, CmClose),
END_RESPONSE_TABLE;

// Класс дочернего окна.
class TChildWndw : public TWindow
{
  public:
    TChildWndw (TWindow *parent, const char far *title, 
         int xCoord, int yCoord);
  protected:
    char far *GetClassName();
    void GetWindowClass (WNDCLASS &wndClass);
};

// TWndw::TWndw()
// Это конструктор главного окна.
TWndw::TWndw(TWindow *parent, const char far *title):
          TFrameWindow (parent, title) 
{
  // Определить расположение и размеры окна.
  Attr.X = 20;
  Attr.Y = 20;
  Attr.W = 500;
  Attr.H = 400;
  // Добавить меню к окну. 
  AssignMenu(MENU_1);
  // Инициализировать начальную позицию дочернего окна.
  childX = 75; 
  childY = 75;
} 

// TWndw::CmOpen()
// Эта функция вызывается, когда пользователь
// выбирает команду Open в меню File.
// Она открывает новое дочернее окно.
void TWndw::CmOpen() 
{
  // Создать новое дочернее окно.
  TWindow *child = new TChildWndw (this, "Дочернее окно", childX, childY);
  // Отобразить окно. 
  child->Create();
  // Вычислить положение следующего окна. 
  childX += 40; 
  if (childX > 400) childX = 75;
  childY += 40; 
  if (childY > 300) childX = 75;
}

// CloseTheWindow()
// Функция ForEach вызывает эту функцию, чтобы 
// закрыть каждое открытое дочернее окно.
void CloseTheWindow (TWindow *window, void*) 
{
  window->ShutDownWindow();
}

// TWndw::CmClose()
// Эта функция реагирует на команду Close All меню 
// File, вызывая итератор ForEach для дочерних окон 
// из списка дочерних окон TWndw. 
void TWndw::CmClose() 
{
  ForEach(CloseTheWindow);
}

//************************************
// Реализация класса TChildWndw.
//************************************
// TChildWndw::TChildWndw()
// Это конструктор дочернего окна.
TChildWndw::TChildWndw (TWindow  *parent,
     const char far *title, int xCoord,  int yCoord) :
            TWindow(parent, title) 
{
  // Установить тип окна.
  Attr.Style = WS_CHILD | WS_VISIBLE | WS_BORDER | 
      WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_CLIPSIBLINGS;
  // Определить расположение и размеры нового окна.
  Attr.X = xCoord;
  Attr.Y = yCoord;
  Attr.W = 200;
  Attr.H = 150;
}

// TChildWndw::GetClassName()
// Эта функция переопределяет GetClassName() из
// TWindow и обеспечивает имя для класса нового окна.
char far *TChildWndw::GetClassName()
{
  return "ChildWindow";
}

// TChildWndw::GeTWindowClass()
// Эта функция переопределяет GetWindowClass() и
// устанавливает атрибуты, отличающиеся от атрибутов,
// задаваемых по умолчанию, класса окна в OWL.
void TChildWndw::GetWindowClass(WNDCLASS &wndClass)
{
  // Разрешить TWindow выполнить его задачу.
  TWindow::GetWindowClass(wndClass);
  // Установить курсор для нового окна.
  wndClass.hCursor = LoadCursor(0, IDC_CROSS);
  // Установить цвет фона для нового класса.
  wndClass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH);
  // Установить пиктограмму для нового класса.
  TApplication  *instance = GetApplication();
  wndClass.hIcon = LoadIcon(*instance, "DOC_ICON");
}
 
void TApp::InitMainWindow()
{
  TFrameWindow *wndw = new TWndw (0,"Приложение с дочерними классами"); 
  SetMainWindow(wndw);
}

int OwlMain(int,char *[])
{
  return TApp().Run();
}

    Файл ресурсов:

#ifndef WORKSHOP_INVOKED
#include "windows.h"
#endif

#define MENU_1   100
#define CM_CLOSE 102
#define CM_OPEN  101
#define CM_EXIT  24310
 
#ifdef RC_INVOKED

MENU_1 MENU
{
  POPUP "&File"
  {
     MENUITEM  "&Open",   CM_OPEN
     MENUITEM  "&Close All", CM_CLOSE
     MENUITEM  SEPARATOR
     MENUITEM "E&xit",    CM_EXIT
  }
}

DOC_ICON ICON "doc.ico"

#endif
Текст этого приложения можно взять здесь.

    Когда вы запустите программу, представленную в примере, вы увидите главное окно приложения.


Рис.1. Результат работы приложения

    При выборе команды Open из меню File появляется новое дочернее окно. Однако эти дочерние окна приложения не являются окнами задаваемого по умолчанию OWL-класса, а, скорее, представляют собой пользовательский оконный класс, имеющий светло-серый фон и курсор в виде перекрестия.

    Цвет фона становится видимым с появлением окна. Чтобы увидеть курсор мыши для нового окна, переместите курсор по окну. Когда курсор мыши проходит через одно из дочерних окон, он изменяется на курсор в виде перекрестия. Более того, эти дочерние окна являются официальными дочерними окнами Windows. Это означает, что они имеют атрибут WS_CHILD и не могут быть вынесены за пределы основного окна.

    Эта программа похожа на пример из предыдущего шага, за исключением того, что сейчас дочернее окно имеет собственный C++ класс:

class TChildWndw : public TWindow
{
  public:
    TChildWndw (TWindow *parent, const char far *title, 
         int xCoord, int yCoord);
  protected:
    char far *GetClassName();
    void GetWindowClass (WNDCLASS &wndClass);
};

    Помимо конструктора, данный класс включает функции-члены GetClassName() и GetWindowClass(), которые переопределяют функции, наследованные из класса TWindow. Именно эти две функции позволяют вам создать ваш собственный пользовательский оконный класс.

    Чтобы создать новый оконный класс, вы должны дать имя этому классу. OWL получает имя класса, вызывал GetClassName():

char far *TChildWndw::GetClassName()
{
  return "ChildWindow";
}

    Так как этот класс только возвращает имя нового оконного класса, нет необходимости вызывать TWindow::GetClassName() в вашей переопределенной функции.

    OWL возвращает атрибуты нового оконного класса, вызывая GetWindowClass():

void TChildWndw::GetWindowClass(WNDCLASS &wndClass)
{
  // Разрешить TWindow выполнить его задачу.
  TWindow::GetWindowClass(wndClass);
  // Установить курсор для нового окна.
  wndClass.hCursor = LoadCursor(0, IDC_CROSS);
  // Установить цвет фона для нового класса.
  wndClass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH);
  // Установить пиктограмму для нового класса.
  TApplication  *instance = GetApplication();
  wndClass.hIcon = LoadIcon(*instance, "DOC_ICON");
}

    GetWindowClass() отвечает за заполнение значений структуры WNDCLASS оконного класса. Эта структура содержит значительно больше членов-данных, чем обычно требуется переустанавливать в переопределеной версии этой функции.

    Поэтому важно вызвать TWindow::GetWindowClass() до выполнения любых нужных вам изменений. Если вы забудете вызывать TWindow::GetWindowClass(), некоторые важные параметры останутся неинициализированными. С другой стороны, если TWindow::GeTWindowClass() вызван после того, как вы задали все установки, ваши изменения будут перезаписаны значениями, задаваемыми OWL по умолчанию.


    Замечание. В стандартных C++-программах, т.е. не OWL-программах, программист должен создать полный оконный класс Windows для каждого типа окна, использованного в приложении. Этот класс создается путем заполнения значений структуры WNDCLASS и последующей регистрации класса в Windows. Следующий фрагмент программы показывает, как выглядит задача регистрации окна в обычной С++-программе:
WNDCLASS wndClass;
// Определить   класс окна.
wndClass.lpszClassName =  "App Name";
wndClass.hInstance = hInstance;
wndClass.lpfnWndProc = WndProc; 
wndClass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndClass.hIcon = LoadIcon (hInstance,   "ICON");  
wndClass.lpszMenuName = NULL;
wndClass.hbrBackground = GetStockObject (WHITE_BRUSH);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.cbClsExtra  =  0; 
wndClass.cbWndExtra = 0;
// Зарегистрировать класс окна. 
RegisterClass   (&wndClass);

    Дополнительную информацию по созданию окон вы можете получить в следующих шагах раздела "Программирование | Язык программирования Ассемблер | 32-битное программирование": 11, 12, 15.


    Итак, эта функция сначала вызывает TWindow::GetWindowClass(), после чего она получает дескриптор системного курсора в виде перекрестия. Функция Windows API LoadCursor() имеет два аргумента. Первый из них - дескриптор экземпляра приложения, владеющего курсором. В случае системного курсора первый аргумент должен быть нулевым. Второй аргумент - long - указатель на строку, содержащую имя курсора. IDC_CROSS является предопределенной константой Windows для этого указателя. Более подробно с этими константами вы ознакомитесь в следующей программе. Новый дескриптор курсора заносится в член-данное wndClass.hCursor.

    После установки курсора GeTWindowClass() изменяет цвет фона на светло-серый. Для этого запрашивается дескриптор светло-серой кисти и заносится в член-данное wndClass.hbrBackground.

    Наконец, программа устанавливает пиктограмму окна в виде пиктограммы документа. Чтобы выполнить эту задачу, программа вызывает функцию-член GetApplication() главного окна, наследуемую из TWindow, для получения указателя на экземпляр приложения. В обращении к LoadIcon() используется разыменованный указатель наряду с именем пиктограммы для получения дескриптора пиктограммы, который затем заносится в член-данное WndClass.hIcon.

    На этом новый оконный класс считается инициализированным и готовым для выполнения. Когда окно появляется на экране, оно будет иметь все атрибуты, которые были установлены в GetWindowClass().

    На следующем шаге мы рассмотрим изменение курсора мыши.




Предыдущий шаг Содержание Следующий шаг