Шаг 47.
Библиотека OWL.
Создание дочерних окон и управление ими

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

    При самостоятельном создании приложений с использованием библиотеки OWL тут же начнут возникать вопросы типа: как создавать дочерние окна и как управлять ими? Как разобраться с линейками прокрутки? Каким образом добавлять эти полезные управляющие элементы при разработке собственных прикладных программ?

    Ответы на эти, а также на многие другие вопросы вы найдете в следующих шагах, в которых глубже рассматриваются возможности OWL-класса TWindow. Здесь мы рассмотрим некоторые еще не знакомые вам функциями, так же как с полезными приемами программирования, которые позволяют использовать возможности Windows в полной мере.

    В процессе обсуждения вопросов программирования в среде Windows и использования OWL возникли неоднозначные толкования термина "дочерние окна". К сожалению, из-за недостаточно исчерпывающих толкований этот термин часто имеет разный смысл, зависящий от контекста, в котором он употреблен.

    К примеру, большинство объектов, возникающих в ваших приложениях: окна, диалоговые окна, управляющие элементы - являются объектами, связанными с классом TWindow, иными словами, эти объекты являются окнами. Поэтому, хотя дочернее окно MDI очевидно является таковым по определению, кнопку диалога иногда также называют дочерним окном.

    Такое определние дочернего окна указывает на взаимосвязь объекта с другим окном. В этом смысле дочернее окно (будь то обычное окно или кнопка) имеет родительское окно. Однако, исходя из классической терминологии Windows, этот тип дочернего окна в действительности имеет не родительское окно, а так называемое окно-владельц. Это различие позволяет установить более-менее однозначное определение дочернего окна.

    В Windows дочернее окно - это окно, имеющее стиль WS_CHILD. Окно данного типа не может быть передвинуто за пределы рабочей области родительского окна, подобно дочернему окну MDI. Такое определение дочернего окна указывает как на стиль окна, так и на тот факт, что это окно имеет родительское окно.

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

  1. Перекрывающиеся окно. У окна данного типа нет родительского окна (хотя может быть окно-владелец). Вы используете перекрывающееся окно, которое обычно имеет меню, строку заголовка и жирную рамку, в качестве главного окна вашего приложения. Перекрывающиеся окна имеют стиль WS_OVERLAPPED или WS_OVERLAPPEDWINDOW.
  2. Всплывающие окна. Эти окна не привязаны к рабочей области окна-владельца. Хотя всплывающее окно и принадлежит другому окну, оно может появляться в любом месте экрана. При минимизации всплывающего окна его пиктограмма появляется на рабочем столе Windows, а не в рабочей области окна-владельца. Всплывающие окна имеют стиль WS_POPUP или WS_POPUPWINDOW.
  3. Дочернее окно. Дочернее окно не может быть вынесено за пределы рабочей области своего родительского окна. Кроме того, при минимизации окна его пиктограмма появляется внутри родительского окна, а не на рабочем столе Windows. Стиль дочернего окна - WS_CHILD.


    Замечание. При создании нового окна у программиста может возникнуть необходимость использования нескольких констант стиля окна, чтобы определить, как окно будет выглядеть и действовать. Например, полностью функциональное перекрывающееся окно может требовать задания следующих типов:
  WS_OVERLAPPED   |   WS_CAPTION   |   WS_SYSMENU   |   WS_THICKFRAME    |
            WS_MINIMIZEBOX   |   WS_MAXIMIZEBOX

    Чтобы избежать необходимости включения отдельной константы для каждого элемента типа окна, Windows определяет константы двух стилей, которые уже включают обычные стили окон. Константа WS_OVERLAPPEDWINDOW определяет перекрывающееся окно. Тип WS_POPUPWINDOW создает всплывающее окно с типами WS_POPUP, WS_BORDER, WS_SYSTEM (системное меню появляется, только если добавить WS_CAPTION).


    Теперь, когда вы знаете отличия дочернего окна от подчиненного, для простоты изложения в дальнейшем будем называть и то и другое как дочернее.

    На 45 шаге вы познакомились с дочерним MDI-окном. Окна данного вида являются дочерними в буквальном смысле слова. Это означает, что они не могут быть перемещены за пределы рабочей области своего родительского окна, при минимизации их пиктограммы появляются внутри родительского окна. Но что делать, если вы захотите создать окно About или другой тип вспомогательного окна? Другими словами, как поступать, если необходимо сконструировать всплывающее окно не в MDI-приложениях. Следующий пример показывает, как это делается.


    Следующий пример показывает, как создавать дочернее окно и использовать функцию-итератор ForEach для управления дочерним окном.
#include <owl\applicat.h>
#include <owl\framewin.h>
#include "pr47_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;

// 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 TWindow (this, "Дочернее окно");
  // Установить стиль окна. 
  child->Attr.Style = WS_VISIBLE | WS_POPUPWINDOW | WS_CAPTION;
  // Определить расположение и размер нового окна. 
  child->Attr.X = childX; 
  child->Attr.Y = childY; 
  child->Attr.W = 200; 
  child->Attr.H = 150;
  // Отобразить окно. 
  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);
}
 
void TApp::InitMainWindow()
{
  TFrameWindow *wndw = new TWndw (0,"Приложение дочерних окон"); 
  SetMainWindow(wndw);
}

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

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

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

#include <owl\mdi.rh>
#include <owl\window.rh>

#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
  }
}

#endif

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

    При запуске этой программы появляется главное окно приложения.


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

    Выберите команду Open из меню File, и появится новое дочернее окно. Помните, что это так называемое дочернее окно в действительности является подчиненным окном, так как оно имеет стиль, отличный от WS_CHILD. Вы можете создать столько дочерних окон, сколько вам захочется, выбирая повторно команду Open, как показано на рисунке 1. Вы можете закрыть каждое дочернее окно в отдельности двойным нажатием кнопки мыши, когда курсор будет находиться в верхнем левом углу дочернего окна, или можно закрыть одновременно все окна, выбрав команду Close All в меню File.

    Посмотрите на класс главного окна:

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;

    Этот класс содержит два защищенных члена-данных - childX и childY, содержащих координаты следующего окна. Кроме конструктора класс TWndw имеет две функции отклика на сообщения CmOpen() и CmClose(), которые реагируют на команды Open и Close All соответственно.

    Как обычно, конструктор TWndw определяет расположение и размеры главного окна прикладной программы и назначает ему меню. Кроме того, эта версия конструктора также инициализирует положение первого дочернего окна:

  childX = 75; 
  childY = 75;

    Конечно, в тот момент, когда программа инициализирует свои childX и childY, дочернее окно еще не существует. Значения этих полей используются в CmOpen(), функции, которую OWL вызывает при выборе команды Open из меню File:

void TWndw::CmOpen() 
{
  // Создать новое дочернее окно.
  TWindow *child = new TWindow (this, "Дочернее окно");
  // Установить стиль окна. 
  child->Attr.Style = WS_VISIBLE | WS_POPUPWINDOW | WS_CAPTION;
  // Определить расположение и размер нового окна. 
  child->Attr.X = childX; 
  child->Attr.Y = childY; 
  child->Attr.W = 200; 
  child->Attr.H = 150;
  // Отобразить окно. 
  child->Create();
  // Вычислить положение следующего окна. 
  childX += 40; 
  if (childX > 400) childX = 75;
  childY += 40; 
  if (childY > 300) childX = 75;
}

    Эта функция сначала вызывает конструктор TWindow, чтобы создать новое дочернее окно. Так как дочернее окно представляет собой "приправу" к TWindow, то программе не требуется для него специального класса. Однако атрибуты окна должны быть обязательно инициализированы. Функция CmOpen() получает доступ к атрибутам нового дочернего окна через указатель окна. Стиль окна устанавливается как WS_VISBLE | WS_POPUPWINDOW | WS_CAPTION, т.е. видимое всплывающее окно со строкой заголовка, системным меню и рамкой. Положение окна определяется величинами, хранящимися в childX и childY. Устанавливаются также ширина и высота окна. Затем программа отображает новое дочернее окно, вызывая функцию Create(), которая создает соответствующий элемент-окно. Наконец, CmOpen() вычисляет координаты следующего дочернего окна.

    Когда пользователь выбирает в меню File команду Close All, OWL вызывает CmClose():

void TWndw::CmClose() 
{
  ForEach(CloseTheWindow);
}

    Эта функция демонстрирует применение функции ForEach(), которая вызывает функцию, задаваемую как единственный аргумент ForEach() для каждого окна из списка дочерних окон. Какого списка? Каждое окно, производное от TWindow, содержит список своих дочерних окон. (Здесь мы имеем в виду дочернее окно в широком смысле.) Следовательно, вы можете использовать удобства, предоставляемые списком дочерних окон, ибо TWindow включает много функций обработки списков дочерних окон, включая

    В этой программе той функцией, которую ForEach() вызывает для каждого окна из списка дочерних окон, является CloseTheWindow():

void CloseTheWindow (TWindow *window, void*) 
{
  window->ShutDownWindow();
}

    Когда ForEach() вызывает CloseTheWindow(), она передает в качестве параметров указатель обрабатываемого в данный момент окна и указатель списка параметров. CloseTheWindow() использует указатель окна для вызова функции окна ShutDownWindow(), которая, в свою очередь, вызывает функцию Destroy() из TWindow, чтобы убрать элемент окна с экрана и удалить объект окна.

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

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




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