Создание многооконных приложения с дочерними окнами.

Подход автора.

МDI Application создать просто - File/New/Other.../Projects/МDI Application и все, но...! Окна MDI приложения, при таком подходе, не связаны друг с другом и неуправляемы из основного окна. Нет возможности задавать индивидуальные свойства для окон и т.д.

Поставим целью создать на базе SDI Aplication многооконное приложения с дочерними окнами - приложение, которое при старте запускает окно в котором можно разместить заставку программы и автономно - по нажатию, например, различных кнопок SpeedButton - запускает рабочие дочерние окна в которох будут размещаться отдельные программы. При старте любой программы окно заставка закрываться, а при закрытии всех программ заставка будет появляться.

В качестве примера такого приложения можно посмотреть программу Jeep.

Приступим к созданию такого приложения:

Создаем (File/New/Application) основное окно приложения (при старте Builder формируется автоматически, если не изменены настройки по умолчанию). Это окно родитель - оно будет служить для запуска других окон приложения - дочерних. В свойстве FormStyle инспектора объектов для формы (F11 - Proporties/FormStyle) устанавливаем fsMDIForm. Сохраняем проект и файлы приложения в выбранной директории с любыми понравившимися именами.

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

В пределах созданного приложения создаем новую форму, которая будет являться дочерним окном.

File/New/Form

Сохраняем новую форму с тем именем, под которым она будет использоваться.

File/Save As.../Например View.cpp

Перейти к отображению основной формы и включить в проект созданную для дочернего окна форму:

File/Include UnitHDR../ В окошке Use Unit выбрать модуль View

Перейти к созданной форме и дать ей имя, например Views

F11 Инспектор Объектов Name / Views

Перейти в меню Borlanda View и выбрать опцию Project Manajer и в ней файл основного проекта и в функции WinMain удалить строку:

Application->CreateForm(__classid(TViews), &Views);

Проверить, что там появилась строка в заголовке:

USEFORM("View.cpp", Views);

Перейти к основному окну и проверить в заголовочном раздели файла .cpp должна появиться опция:

#include           "View.h"

Этуже строку скопируем в файл .h основной формы.

Перейти в файл View.h и убирать в нем строку.

extern PACKAGE TViews *Views;

На этом этапе создан класс TViews, запоминаем для себя его имя:

class TViews : public TForm

В инспекторе объектов устанавливаем следушие Properties:

WindowState - wsMaximized
FormStyle   - fsMDIChild

Вновь переходим к форме основного окна и создаем средства для вызова дочернего окна. Это можно сделать для заставки через обработку события OnTimer, естественно поместив на форму компонент TTimer(вкладка System):

void __fastcall TViews::Timer1Timer(TObject *Sender)
{
 if(!fView)
 {
  fView=true;
  Views = new TViews(Application);
  HWND H1=Handle;
  Views->SendHwnd(H1);
 }
}

Для всех следующих создаваемых классов окон (естественно создаваемых таким же образом как и class TViews : public TForm) изменится только имя класса - окна новой программы, но принцип взаимодействия и последовательность операций создания не изменится.

Пусть последующие окна для программ запускаются по нажатию кнопок, (из вкладки Additional компонент SpeedButton) в обработчике события нажатия кнопки (2 левых клика на кнопке) будем записывать примерно такой же код (где fViewN - номер флага, для контроля открытого очередного окна, а окна для следующих программ будут ViewsN):

void __fastcall TViews::SpeedButton1Click(TObject *Sender)
{
 if(!fView1)
 {
  SpeedButton1->Enabled=false;
  fView1=true;
  Views1 = new TViews1(Application);
  HWND H1=Handle;
  Views1->SendHwnd(H1);
 }
}

Здесь, в обоих случаях, создается новый объект класса TViewsN и ему отсылается для запоминания HWND приложения, который понадобится для того, чтобы сообщить основному приложению о закрытии дочернего окна.

В файле .h основного окна естественно должен быть определен флаг и определение объекта:

bool          fViews;
bool          fViews1;
bool          fViews2;
.....
TViews*       Views;
TViews1*      Views1;
TViews2*      Views2;
......

При создании формы основного окна флагу должно быть присвоено значение false, например в FormCreate.

После добавления необходимого кода файл View.h для создаваемых окон будет выглядеть следующим образом (аналог для всех окон):

class
TViews : public TForm
{
 __published:
 private:
  HWND                  Hwnd;
 public:
  virtual   __fastcall  TViews(TComponent* Owner);
  void      __fastcall  SendHwnd(HWND H);
};

А в файле Viev.cpp должен присутствовать и отсутствовать следующие элементы:

1. В заголовке убрана или закоментирована строка:
//TViews        *Views;
2. Написано тело функции:
void __fastcall
TViews::SendHwnd(HWND H1)
{
 Hwnd=H1;
}
2. В функции закрытия дочернего окна или обработки события OnClose:
SendMessage(Hwnd,WM_USER+1,0,0);
Action = caFree;

Заключительный штрих - Оформить реакцию на закрытия дочернего окна в основном окне. Для этого в файле .h основного окна дописываем.

private:
 .............
  void   __fastcall   OtvetN(TMessage &Message);
public:
 .............
BEGIN_MESSAGE_MAP
 MESSAGE_HANDLER(WM_USER+N, TMessage,OtvetN)
END_MESSAGE_MAP(TForm)

Для каждого следующего окна - WM_USER+N, OtvetN.

В функции .cpp

void __fastcall
TOperStat::OtvetN(TMessage &Message)
{
 SpeedButtonN->Enabled=true;
 fViewsN=false;
}

С этого момента при старте создается дочернее окно с заставкой (куда можно поместить рисунок) или по нажатию кнопок (например SpeedButtonN) - если еще создано несколько форм - создаются дочерние окна и после его закрытия можно вновь его создать.

Естественно в обработчике события OnTimer необходим учет всех открытых окон.

if(!fView && !fView1 && !fView2 ....)
{
 fView=true;
 Views = new TViews(Application);
 HWND H1=Handle;
 Views->SendHwnd(H1);
}

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

Для того, чтобы избежать дублирование общих для разных программ функций, достаточно объявлять классы дочерних окон как friden классы.

В начало

Домой