Глава 3

Приложение и формы

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

Стандартный проект в Delphi обычно состоит из форм. Форма — это всего лишь окно приложения на этапе разработки. Каждую форму описывают два файла — файл формы, описывающий свойства формы и размещенных на ней компонентов, и модуль, содержащий описание экземпляра класса формы и исходный код.

Свойства и поведение окна инкапсулированы в очень важном классе TForm, который является отдаленным потомком класса TWinControi (см. гл. 2). Благодаря возможностям своих предков окно приложения Delphi не только обеспечивает работу пользовательского интерфейса, но и является контейнером для других компонентов, организуя управление ими на этапах разработки и выполнения.

В этой главе обсуждаются следующие вопросы.

Роль формы в приложении

Итак, классическое приложение Windows должно иметь хотя бы одно окно. Окно приложения должно уметь выполнять целый ряд важных операций. Оно должно правильно отображать себя на экране, уметь взаимодействовать с другими окнами и операционной системой, управлять размещенными на нем элементами управления, реагировать на разнообразные события.

В среде разработки Delphi понятие окна приложения совмещено с понятием формы. По существу это одно и то же. Просто формой называется окно приложения на этапе разработки. Форме при разработке и окну при выполнении приложения соответствует один и тот же экземпляр класса, тем не менее, введение понятия "форма" представляется оправданным.

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

Самые важные из них заключаются в том, что форма умеет взаимодействовать с инструментами среды разработки (Палитрой компонентов, Инспектором объектов и т. д.) и размещенными на ней компонентами. Первое из названных свойств обеспечивается возможностями, унаследованными от базового класса иерархии VCL TComponent. Второе свойство обеспечено механизмами самого класса формы.

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

Для хранения каждой формы проекта используются два файла. Файл с расширением dfm хранит описание свойств формы и размещенных на ней компонентов. Для хранения исходного кода класса формы и ее компонентов предназначен файл модуля с расширением раз.

Рис. 3.1. Иерархия предков класса TForm

Формы играют исключительно важную роль в процессе создания приложения. Они обеспечивают создание пользовательского интерфейса с одной стороны, и хранение программной логики приложения — с другой. Хотя форма и является потомком класса TComponent (рис. 3.1), она не присутствует в Палитре компонентов. Для создания новой формы проекта необходимо использовать команду New Form из меню File или Репозиторий, где хранятся прототипы часто используемых форм. Это говорит о роли формы в процессе разработки приложения — на нее возложено гораздо больше обязанностей, чем на обычный компонент.

С точки зрения исходного кода, любой размещенный на форме компонент представляет собой всего лишь новую переменную объектного типа, объявленную в классе формы. Это объявление осуществляется автоматически в момент размещения компонента на форме. В результате в модуле этой формы сразу же становятся доступными все свойства и методы этих компонентов. Форма, на которой находится компонент, становится его владельцем.

Форма, как владелец размещенных на ней компонентов, позволяет эффективно управлять ими на этапе выполнения приложения. Для этого класс формы имеет ряд свойств и методов (см. ниже), которые позволяют получить доступ к любому компоненту. Например, программными средствами можно передавать фокус от одного элемента управления другому; выполнять для всех компонентов или их любого подмножества заданную последовательность действий.

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

В зависимости от настроек свойств, формы в проекте могут играть разные роли. Форма может быть главной формой проекта. Для этого необходимо (рассматривается принятый в Delphi интерфейс SDI), чтобы форма в файле проекта создавалась первой. В этом случае она автоматически появляется при запуске приложения, а ее закрытие приводит к завершению работы всей программы.

Дочерние формы равноправны между собой. Их можно запускать в модальном и не модальном режиме. Форма в модальном режиме не позволяет передавать фокус управления элементам управления других форм приложения. Это можно сделать только после закрытия формы. Свободное переключение возможно между формами, работающими в не модальном режиме.

Также формы в приложении различаются по стилю оформления. Это может быть классическое окно с заголовком, рамкой, системными меню и кнопками или диалог и т. д. (см. ниже).

На этапе выполнения форма активно взаимодействует с классом приложения, так как именно через него форма получает все системные сообщения и взаимодействует с другими формами и внешней средой. В этом процессе важную роль играет дескриптор окна, обеспечивающий его идентификацию на программном уровне. Дальше мы детально рассмотрим возможности, которые предоставляет разработчику класс формыт Form.

Класс TForm

Форма в Delphi — это синоним окна. В некоторых библиотеках классов для Windows существуют отдельные классы для каждой важной разновидности окон — диалогового окна, клиентских и дочерних окон MDI и так далее. Разработчики VCL пошли по новому пути, объединив в одном классе тгогт свойства и методы всех этих разновидностей. Специализация при этом все равно осталась — она определяется свойством:

property FormStyle: TFormStyle;

TFormStyle = (fsNormal, fsMDIChild, fsMDIForm, fsStayOnTop);

Здесь варианты означают:

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

У TForm есть методы, которые имеют смысл только для некоторых из стилей Formstyle. Рассмотрим сначала общие свойства и методы формы, а затем — специфические.

Набор кнопок в заголовке окна описывается свойством.

property Borderlcons: TBorderIcons;

TBorderIcon = (biSystemMenu, biMinimize, biMaximize, biHelp);

TBorderIcons = set of TBorderIcon;

По умолчанию, у обычной формы имеются три кнопки — для вызова сис-. темного меню, разворачивания и сворачивания окна. Кнопка закрытия не описана, так как она присутствует всегда при наличии у формызаголовка. О применении кнопки biHelp рассказано ниже в этой главе.

Обрамление формы задается свойством:

property BorderStyle: TFormBorderStyle;

TFormBorderStyle = (bsNone, bsSingle, bsSizeable, bsDialog, bsToolWindow, bsSizeToolWin);

Особенности реализации здесь заключаются в том, что во время разработки вы не видите различий этих стилей — изменения в форме происходят только при выполнении приложения. Стиль обрамления может также изменяться в процессе выполнения программы.

Рассмотрим возможные стили обрамления.

Таблица 3.1. Стили обрамления форм

Тип формы

Описание

bsNone

Форма представляет собой прямоугольник без рамки, заголовка и системных кнопок в заголовке. Размеры такой формы нельзя изменить без специальных приемов

bsSingle

Стандартное окно без возможности изменения размеров. Набор системных кнопок зависит от свойства Borderlcons

bsSizeable

Значение по умолчанию. Стандартная форма с возможностью изменения размеров. Набор системных кнопок зависит от свойства Borderlcons

bsDiaiog

Обрамление стандартного диалогового окна. В наборе системных кнопок отсутствуют biSystemMenu, biMinimize И biMaximize

bsToolWindow

Обрамление панели инструментов. Идентично стилю bssingle, но с уменьшенной высотой заголовка и системными элементами

BsSizeToolWin

Обрамление панели инструментов. Идентично стилю bsSize-able, но с уменьшенной высотой заголовка и системными элементами

Форма с обрамлением bsNone может с успехом применяться для создания форм-заставок (так называемых splash screens) для больших, долго загружас мых приложении. Примеры использования таких заставок — Delphi, все продукты из состава Microsoft Office и т. п. Достаточно создать в проекте дополнительную форму, установить BorderStyle в bsNone, свойство Position в poscreencenter, написать на ней что-нибудь вроде "Подождите, идет загрузка приложения", и заставка готова. Теперь включим ее использование в файл текста проекта (*.dpr):

SplashForm := TSplashForm.Create(Application);

SplashForm.Visible := True;

SplashForm.ShowLoadingProcess('Создание приложения...');

Application.Initialize;

SplashForm.ShowLoadingProcess('Подключение базы...' ); Application.CreateFormfTDataModulel, DataModulel);

Application.CreateFormfTMainForm, MainForm) ;

SplashForm.ShowLoadingProcess('Создание форм...');

Application.CreateForm(TOptionsForm, OptionsForm);

Application.CreateForm(TEnterNewResForm, EnterNewResForm);

Application.CreateForm(TGroupTimeReport, GroupTimeReport);

Application.CreateFormfTDailyReportForm, DailyReportForm);

Application.CreateForm(TMonthlyReportForm, MonthlyReportForm);

SplashForm.Visible := False;

SplashForm.Free ;

Application.Run;

В этом примере к форме SplashForm добавлен метод ShowLoadingProcess, который позволяет динамически отображать ход загрузки. Можно также добавить компоненты вроде TprogressBar или TAnimate, чтобы скрасить пользователю ожидание загрузки. С Delphi поставляется пример использования заставок — приложение MastApp.

Каждое окно может находиться в одном из состояний — нормальном, свернутом (минимизированном) и развернутом (максимизированном), что определяется свойством:

property WindowState: TWindowState;

TWindowState = (wsNormal, wsMinimized, wsMaximized);

Значение этого свойства можно изменить как на этапе разработки, так и во время исполнения.

Форма в свернутом состоянии на экране отображается значком, который задается в свойстве:

property Icon: TIcon;

Приложение идентифицируется в полосе задач Windows не значком главной формы, а своим значком (Application, icon). Его можно задать в диалоговом окне свойств проекта (Project\Options\Application) или программно.

Каждая форма может иметь главное и всплывающее меню. Главное меню определяется свойством:

property Menu: TMainMenu;

Оно может быть у каждой формы (и только у формы). Для того чтобы не возникало путаницы с несколькими одновременно видимыми меню в одном приложении, у компонентов TMainMenu предусмотрена возможность объединения в одном; она описана в главе 5 "Меню".

Всплывающее меню формы представлено свойством:

property PopupMenu: TPopupMenu;

Форма может работать с интерфейсом OLE Drag-and-Drop. Для этого она должна быть зарегистрирована в качестве приемника объектов. Свойство

property DropTarget: Boolean;

устанавливает, может ли форма быть приемником объектов OLE.

Создание формы

В стандартном приложении задача создания и удаления форм возлагается на само приложение. Правила создания форм на этапе выполнения задаются в параметрах проекта (страница Forms). Все формы, которые вы включили в состав приложения на этапе визуального проектирования, могут быть автоматически созданы при его запуске, для этого их имена должны находиться в списке Auto-create forms (рис. 3.2). Все прочие формы (список Available forms) нужно создавать самостоятельно, вызвав конструктор Create или метод Application. CreateForm.

Рис. 3.2. Параметры создания форм можно задать в диалоговом окне свойств проекта

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

property OnCreate: TNotifyEvent;

Это событие инициируется в конструкторе формы и позволяет выполнить операции по начальной установке формы и ее элементов до визуализации. В обработчик этого события стоит включить присвоение значений неопубликованным свойствам формы и свойствам компонентов, которых нет в палитре. Также здесь нужно "подключить" написанные вами обработчики событий для этих компонентов. В этом случае внешний вид и поведение форм будут корректными с самого начала.

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

Остальные формы из списка Auto-create forms будут созданы, но не видимы. Для их показа и активизации необходимо вызвать один из методов — show или showModal. Последний используется только для модальных форм, работа с которыми описана ниже.

Если в проекте много форм, постарайтесь не создавать все сразу и перенести максимально возможное количество из списка Auto-create forms в список Available forms. Это позволяет сильно экономить память и, в конечном счете, — время.

При вызове метода

procedure Show;

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

property OnShow: TNotifyEvent;

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

property Position: TPosition;

TPosition = (poDesigned, poDefault, poDefaultPosOnly, poDefaultSizeOnly, ooScreenCenter, poDesktopCenter, poMainFormCenter, poOwnerFormCenter);

Возможные значения свойства объясняются в табл. 3.2.

Таблица 3.2. Возможные значения свойства формы position

Значение

Применение

poDesigned

Принять те значения положения и размеров, которые были определены во время разработки приложения

poDefault

Принять положение и размеры, определенные средой Windows по умолчанию

poDefaultPosOnly

Принять только положение, определенное средой Windows по умолчанию

poDefaultSizeOnly

Принять только размеры, определенные средой Windows пo умолчанию

poScreenCenter

Поместить окно по центру экрана. В случае наличия нескольких мониторов в системе, форма размещается в центре монитора по умолчанию

poDesktopCenter

Поместить окно по центру рабочего стола (в том числе составленного из нескольких мониторов)

poMainFormCenter

Поместить окно по центру главной формы приложения

poOwnerFormCenter

Поместить окно по центру формы-владельца

 По умолчанию значение свойства равно poDesigned. Если вы хотите рас пространять свое приложение, не забудьте изменить poDesigned на более подходящее значение. Забавно наблюдать, как начинающие программисты ищут форму, созданную при разрешении 1600х1200, на клиентской машине с разрешением 640х480.

Еще несколько слов о многомониторных системах. Свойство

property DefaultMonitor: TDefaultMonitor;

type TDefaultMonitor = (dmDesktop, dmPrimary, dmMainForm, dmActiveForm) ;

определяет, на каком из нескольких мониторов появится форма:

О характеристиках текущего монитора можно узнать из свойства

property Monitor: TMonitor;

Объект TMonitor, в свою очередь, имеет свойства width. Height. Left, Top (логические координаты начала рабочей области монитора в цепочке из нескольких), MonitorNum И Т. П.

Если ваш интерес к нескольким мониторам не праздный, описание соответствующего API и структур имеется в файле Multimon. раs.

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

property OnPaint: TNotifyEvent;

Для этого он должен использовать канву формы;

property Canvas: TCanvas;

Обработчик события onPamt вызывается до того, как начнется отрисовка всех компонентов, принадлежащих форме. Поэтому нарисованное в нем может послужить фоном. Пример:

procedure TFormI.FormCreate(Sender: TObject);

begin gr := TBitmap.Create;

gr.LoadFromFile('D:\Program Files\Borland\Borland Shared\Images\Spiash\256Color\ chemical.bmp') ;

end;

procedure TFormI.FormPaint(Sender: TObject);

begin Canvas.Draw(0,0,gr);

end;

После отрисовки форма получает фокус ввода сообщений от системных устройств, т. е. становится активной. Об этом извещает наступление события:

property OnActivate: TNotifyEvent;

Узнать, активна ли форма, можно путем обращения к ее свойству:

property Active: Boolean;

При активизации формы ей посылается сообщение о настройке ее размеров (wm_size.). После того как размеры изменены, возникает событие:

property OnResize: TNotifyEvent;

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

После изменения размеров (если таковое имело место) вновь возникает событие OnPaint.

Разумеется, описанные выше события (onPaint, OnActivate и OnShow) возникают не только при инициализации формы, а и, к примеру, при каждом переключении между приложениями или формами в приложении. Последние два действия имеют парные события, возникающие при утере фокуса или исчезновении формы с экрана:

property OnDeactivate: TNotifyEvent;

property OnHide: TNotifyEvent;

Метод

procedure Hide;

дает возможность сделать форму невидимой (изменяет свойство visible). Во всех случаях, когда форма становится невидимой, возникает событие OnHide.

С помощью пары методов show/Hide обычно управляют из главной формы показом прочих форм. Будьте внимательны! При вызове Hide для главной формы подумайте о том, каким путем вы собираетесь после этого снова визуализировать ее.

Основной способ, используемый для закрытия формы, — это вызов метода

Close:

procedure Close;

В первую очередь внутри close вызывается метод:

function CloseQuery: Boolean;

Для закрытия формы необходимо, чтобы он вернул True. Для проверки на возможность закрытия он вызывает обработчик события:

property OnCloseQuery: TCloseQueryEvent;

TCloseQueryEvent = procedure(Sender: TObject;

var

CanClose: Boolean) ofobject;

Здесь вы должны проверить возможность закрытия формы и вернуть нужное значение в параметре canciose (который изначально имеет значение True). Например:

procedure TForml.FormCloseQuery(Sender: TObject;

var

CanClose: Boolean);

begin

if Memo1.Modified then case MessageDlgC Save text?', mtWarning, mbYesNoCancel, 0) of mrYes: Memol.Lines.SaveToFile('MYTEXT.TXT');

mrCancel: CanClose := False;

end;

end;

Таким образом, если обработчик события OncioseQuery не описан, метод closeQuery возвращает True и процесс закрытия формы можно продолжать.

Для форм стиля fsMDiForm (родительских окон MDI) сначала должны закрываться все дочерние формы (вызываются их методы closeQuery). Если хотя бы одна из них возвращает False, метод тут же возвращает False, и обработчик события OncioseQuery закрываемой родительской формы управления не получает.

Затем метод close вызывает обработчик следующего события:

property OnClose: TCloseEvent;

TCloseEvent = procedure(Sender: TObject; var Action : TCloseAction) of object;

TCloseAction = (caNone, caHide, caFree, caMinimize) ;

что позволяет определить дальнейшую реакцию. Дело в том, что закрытие можно реализовать различными способами, в зависимости от значения параметра Action:

Перед вызовом OnClose в параметр Action засылается значение caHide. Исключение составляют дочерние формы MDI, для которых засылается значение caMinimize или caNone в зависимости от возможности их сворачивания.

Если обработчик вернул значение caNone, форма не закрывается. Таким путем можно продублировать возможность обработчика OncioseQuery не закрывать форму.

Если же обработчик вернул отличное от caNone значение, дальнейшая логика такова. При закрытии главной формы вызывается процедура завершения приложения. Для формы, не являющейся главной, значения caHide и caMinimize-позволяют соответственно скрыть и свернуть ее в значок. Выбор значения caFree влечет за собой вызов деструктора путем обращения к методу Release.

Метод Release используется также при необходимости дать команду на уничтожение формы внутри метода-обработчика событий. Дело в том, что он посылает окну формы сообщение cm_release, закрывающее форму. Но поскольку его обработка произойдет не немедленно, а тогда, когда получит управление функция — обработчик сообщений, до этого времени успеет корректно завершиться метод, вызывавший Release.

Последним при уничтожении формы инициализируется событие:

property OnDestroy: TNotifyEvent;

Это событие является парным по отношению к oncreate; обработчик его вызывается в деструкторе формы. Здесь программист может предусмотреть необходимые действия при уничтожении формы, к примеру, записать значения настроек в системный реестр или в файл . ini.

Модальные формы

Компонент TForm в VCL объединяет в себе свойства и может играть роль как обычного окна программы, так и диалогового (модального) окна. Модальная форма отличается тем, что при ее показе она принимает фокус ввода; вплоть до ее закрытия переключиться на другие формы приложения невозможно. Чтобы форма работала в таком качестве, вместо метода show нужно вызвать метод:

function ShowModal: Integer;

Этот метод реализует весь жизненный цикл модальной формы. Он показывает ее на экране, активизирует ее и запрещает переключение на другие формы приложения. Начинается обработка сообщений от элементов управления, которая происходит до тех пор, пока свойство:

property ModalResult: TModalResult;

не изменит своего значения, первоначально нулевого. Это свойство формы может изменяться непосредственно с помощью некоторых видов принадлежащих ей кнопок (классы TButton и TBitBtn). У этих кнопок также есть свойство ModalResult; при нажатии его значение передается одноименному свойству формы, и она закрывается.

Если для модальной формы программистом вызывается метод close, то ее единственным действием является то, что свойству ModalResult присваивается значение mrCancel (без вызовов OnCloseQuery И OnClose).

Как только получено ненулевое значение ModalResult, вызывается метод:

procedure CloseModal;

Его роль такая же, как у close для обычной формы: сначала генерация события cmcioseQuery, затем — Onciose. Установив в параметре Action этого события значение caNone, можно обнулить ModalResult и тем самым воспрепятствовать закрытию. В противном случае форма деактивизируется и делается невидимой.

Метод showModal и сопутствующий ему набор методов неприменим к дочерним формам MDI (со стилем fsMDichild).

Управление дочерними элементами

Если вы хотите передать фокус одной из форм проекта, используйте метод этой формы:

procedure SetFocus;

Форма при этом обязана быть видимой и активной (Enabled), иначе создается исключительная ситуация EinvalidOperation.

Два метода отвечают за передачу фокуса между визуальными компонентами в составе формы.

Метод

procedure FocusControl(Control: TWinControl);

передает фокус элементу управления control, а метод

procedure DefocusControl(Control: TWinControl;

Removing: Boolean);

отбирает его. Параметр Removing, равный True, означает, что элемент при этом должен передать фокус своему родительскому элементу. Напомним, что для элементов в эти моменты генерируются события onEnter и onExit. Имеющий фокус элемент определяется свойством:

property ActiveControl: TWinControl;

Оно может принимать значение Nil, если фокус имеет сама форма.

Форма относится к компонентам с возможностью прокрутки содержимого клиентской области. К полосам прокрутки можно обратиться как к отдельным объектам с помощью свойств:

property HorzScrollBar;

property VertScrollBar;

Если все элементы управления не могут быть видны при данных размерах формы, она может быть снабжена полосами прокрутки автоматически. Это происходит, если значение свойства:

property AutoScroll;

установлено равным True. Следующий метод помещает элемент управления AControl в видимую часть клиентской области:

procedure ScrollInView(AControl: TControl);

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

Прокрутку (скроллинг) элементов на интервал Deltax, DeltaY осуществляет метод:

procedure ScrollBy(DeltaX, DeltaY: Integer);

Прибегать к вызову этой процедуры можно при желании осуществить прокрутку нестандартным способом, т. е. без привлечения полос прокрутки. Приведенный ниже фрагмент кода позволяет "тащить" изображение imagel вслед за мышью с нажатой кнопкой:

type

TMouseState = (msNormal, msDragging);

var OldPos, NewPos, MaxShift: TPoint;

FMouseState : TMouseState;

procedure TForml.ScrollMouseDown(Sender: TObject;.Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

begin

MaxShift.X := Imagel.Parent.Width - Imagel.Width;

MaxShift.Y := Imagel.Parent.Height - Imagel.Height;

if (MaxShift.X > 0) and (MaxShift.Y > 0) then Exit;

FMouseState := msDragging;

OldPos := Point(X, Y);

Screen.Cursor := crDrag;

end;

procedure TFoml. ScrollMouseMove (Sender: TObject; Shift: TShiftState; X, Y: Integer);

begin

if FMouseState = msDragging then

begin

NewPos := Point(X - OldPos.X, Y - OldPos.Y);

if Imagel.Left + NewPos.X > 0 then NewPos.X := - Imagel.Left;

if Imagel.Left + NewPos.X < MaxShift.X

then NewPos.X := MaxShift.X - Imagel.Left;

if Imagel.Top + NewPos.Y > 0 then NewPos.Y := - Imagel.Top;

if Imagel.Top + NewPos.Y < MaxShift.Y

then NewPos.Y := MaxShift.Y - Imagel.Top;

Imagel.Parent.ScrollBy(NewPos.X, NewPos.Y) ;

end;

end;

procedure TFormI.ScrollMouseUp(Sender: TObject; Button: TMouseButton;

Shift: TShiftState; X, Y: Integer);

begin

FMouseState := iiisNormal;

Screen.Cursor := crDefault;

end;

Обратите внимание, что прокрутка неоконного компонента image 1 осуществляется посредством вызова imagel. parent. ScrollBy. Это свидетельствует о том, что конкретный родительский тип для этого безразличен. В примере изображение помещено на панель (трапе!). Впрочем, метод ScrollBy используется также и полосами прокрутки, которые есть в компоненте TScrollingWinControi и его потомках, например, в TForm.

Форма имеет возможность перехватить ввод от клавиатуры, предназначенный имеющему фокус элементу управления. Установка значения свойства

property KeyPreview: Boolean;

в True означает, что поступающие от клавиатуры сообщения будут сначала поступать форме, и только затем — элементу управления.

Масштабирование

Немаловажной проблемой является то, как заставить форму и ее элементы выглядеть одинаково при различных режимах работы видеоадаптера. За это отвечают два свойства:

property Scaled: Boolean; property PixelsPerInch: Integer;

В принятом по умолчанию варианте (scaled = True) при создании формы значение PixelsPerInch считывается из текущих настроек системы, и, при необходимости, форма и все ее элементы масштабируются в некоторой пропорции. Эта пропорция равна отношению числа точек на дюйм на экране в данный момент к этому же числу во время разработки. Для стандартных шрифтов значение PixelsPerInch равно 96, для больших шрифтов — 120. Если же установить PixelsPerInch = о или scaled = False, никакого масштабирования не производится.

Внешний вид формы и ее составных частей можно отобразить не только на экране.

Метод

function GetFormImage: TBitmap;

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

procedure Print;

Полученное изображение передается объекту TPrinter (см. ниже), при этом его масштабирование производится в соответствии со значением свойства:

property PrintScale: TPrintScale;

TPrintScale = (poNone, poProportional, poPrintToFit);

где значения имеют такой смысл:

Каждая форма может быть связана с системой помощи использованием двух свойств:

property HelpFile: string;

property HelpContext: THelpContext;

При нажатии кнопки <F1> вызывается система помощи Windows, которая открывает файл HelpFile и в нем страницу HelpContext.

Можно вызвать помощь и другим способом: щелкнув по пиктограмме Help в заголовке формы (на ней изображен значок вопроса). Для этого нужно соблюдение следующих условий:

После выполнения этих условий при щелчке на пиктограмме Help курсор приобретает особый вид (со знаком вопроса). Щелкнув таким курсором в нужном месте формы, вы получаете контекстную подсказку.

Шаблоны форм

Многие приложения в процессе работы выполняют некоторые стандартные операции. Для этого они имеют формы, содержащие примерно одинаковые наборы элементов управления. Часть операций приложение должно обеспечивать стандартными системными диалогами (открытие и сохранение файлов, печать и т. д.). О стандартных системных диалогах мы поговорим ниже, а пока остановимся на шаблонах форм.

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

Наиболее часто используемые при создании приложений шаблоны форм, разработанные программистами фирмы Inprise, помещены в Репозиторий на страницах Forms и Dialogs. Шаблоны на этих страницах позволяют стандартизовать интерфейсы приложений, обеспечивают эффективное повторное использование кода и экономят время для решения более сложных вопросов программирования. Шаблоны хранятся" в папке с именем objrepos и представляют собой обычные файлы форм.

Шаблоны форм Репозитория можно использовать тремя различными способами.

Для каждого открытого в среде разработки проекта в Репозиторий автоматически создается одноименная страница, которая содержит все существующие в проекте формы. Любую из этих форм можно использовать в качестве заготовки для новой формы. Обратите внимание, что формы на странице проекта доступны для использования только в режиме наследования (в нижней части страницы активна только кнопка Inherit).

Кроме того, какую-нибудь особенно полезную форму можно включить в Репозиторий для постоянного использования в любом проекте. Для этого используется команда Add to Repository меню Project Главного окна Delphi. В диалоге этой команды необходимо выбрать страницу Репозитория и ввести справочные сведения о форме (рис. 3.3).

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

Рис. 3.3. Диалоговое окно регистрации шаблона формы для Репозитория

На странице Dialogs Репозитория также имеется значок мастера создания диалога Dialog Wizard. Он достаточно прост и позволяет легко и быстро создать форму с многостраничным блокнотом или без него, с кнопками или без них.

Многодокументный интерфейс

Многодокументный интерфейс Multi Document Interface (MDI) появился достаточно рано — в MS Excel версии 2.0. Его появление было обусловлено тем, что надо было дать пользователю возможность работать с разными документами, используя одни и те же функциональные возможности. В MDI все окна, кроме главного, являются дочерними и отображаются исключительно в рабочей области главного окна. Дочерние окна не могут существовать без главного — при закрытии приложения и уничтожении главного окна все открытые дочерние окна уничтожаются автоматически. При сворачивании главного окна все дочерние окна также сворачиваются. При сворачивании дочернего окна главное остается без изменений, а значок дочернего окна размещается в нижней части главного.

К настоящему времени стандартный внешний вид главного окна интерфейса MDI также сформировался. Главное окно имеет меню со стандартными командами, панель инструментов в верхней части и строку состояния в нижней части. В Репозитории Delphi на странице Projects имеется шаблон приложения с многодокументным интерфейсом.

Многодокументный интерфейс определяется свойствами главного и дочерних окон. Основным является свойство FormStyle формы. Для дочернего окна MDI оно должно иметь значение fsMDichild. Для главного окна MDI свойство должно иметь значение fsMDiForm.

При использовании шаблона многодокументного приложения из Репозитория в проект автоматически добавляется форма MDichild стандартного дочернего окна. Модуль этой формы по умолчанию называется chiidwin. pas. Разработчик может модифицировать эту форму в соответствии с собственными потребностями. Тогда все создаваемые в приложении дочерние формы будут обладать требуемым набором свойств и элементов управления.

Перечисленные ниже свойства и методы работают только в главных окнах приложений MDI (табл. 3.3).

Таблица 3.3. Свойства и методы класса тForm для реализации многодокументного интерфейса

Свойство или метод

Описание

property ClientHandle: HWND;

Содержит дескриптор клиентского окна (для главного окна mdi)

procedure Arrangelcons;

Упорядочивает расположение свернутых в значок дочерних форм MDI

procedure Cascade;

Располагает дочерние формы MDI каскадом Располагает дочерние формы MDI мозаикой.

procedure Tile;

Стиль определяется свойством TileMode

property TileMode: TTileMode; TTileMode = (tbHorizontal, tbVertical);

Определяет стиль расположения дочерних форм — вертикальный или горизонтальный

procedure Next;

Передает фокус следующей форме в списке дочерних форм

procedure Previous;

Передает фокус предыдущей форме в списке дочерних форм

property ActiveMDIChild: TForm;

Содержит указатель на активную дочернюю форму

property MDIChildCount: Integer-property MDIChildren [I: Integer]: TForm;

Определяет количество дочерних форм Содержит указатель на 1-тую дочернюю форму

property WindowMenu: TMenuItem;

Указывает на тот пункт меню главной формы, к которому достраивается список имен дочерних форм

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

procedure TMainForm.CreateMDIChild(const Name: string);

var

Child: TMDIChild;

begin

( create a new MDI child window }

Child := TMDIChild.Create(Application);

Child.Caption := Name;

if PileExists(Name) then Child.Memol.Lines.LoadFromFile(Name);

end;

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

Итак, для того чтобы создать приложение с многодокументным интерфейсом, достаточно использовать соответствующий шаблон из Репозитория. Стандартную дочернюю форму можно настроить в соответствии с задачами приложения. Например, компонент тмето для представления текстов дочерней формы шаблона MDI можно заменить на компонент т image для представления изображения. Тогда все созданные в работающем приложении дочерние формы будут иметь одинаковые возможности и внешний вид.

При необходимости создать нестандартную дочернюю форму требуется только установить для свойства FormStyle значение fsMDichild.

Однодокументный интерфейс

Однодокументный интерфейс Single Document Interface (SDI) определяет еще один способ взаимодействия главного и дочерних окон приложения. Он явля-

ется более универсальным и в настоящее время получил широкое распространение. Главная форма SDI появляется при запуске приложения и должна объединять все важнейшие функции управления. Из нее же запускаются дочерние формы. Дочерние формы пользуются относительной самостоятельностью и связаны с главной формой через данные. Дочерние формы могут размещаться в любой области экрана, даже перекрывая главную форму. Также дочерние формы можно запускать из других дочерних форм. Перед сворачиванием главного окна SDI сворачиваются все дочерние формы. Прекрасным примером приложения с интерфейсом SDI является среда разработки Delphi.

В приложениях SDI свойства, рассмотренные выше и обеспечивающие работу с дочерними приложениями, не используются.

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

Интерфейс SDI выбран в качестве стандартного в проектах Delphi. Все формы приложения SDI для свойства FormStyle имеют значение fsNormai. Главной считается форма, которая создается первой. Для задания главной формы можно использовать диалог настройки параметров проекта (команда Options из меню Project).

С точки зрения разработчика интерфейс SDI проще в реализации и удобнее в работе. В Репозитории Delphi на странице Projects имеется шаблон приложения SDI. От нового стандартного приложения он отличается тем, что главная форма уже имеет необходимые элементы управления: меню, панель инструментов, панель состояния.

Кадры (Frames). Класс TFrame

В Delphi 5 появилась новая удобная возможность конструировать формы и типовые наборы компонентов — кадры (frames). Что из себя представляет кадр? С точки зрения объектно-ориентированного программирования, это класс-потомок TScrollingWinControi, то есть "двоюродный брат" формы.

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

Первым компонентом на первой(!) странице Standard в Delphi 5 является Frames. Но если вы, решив познакомиться с этой новинкой, просто попытаетесь перенести ее на форму, вас ждет разочарование — сообщение "No frames in project. To create a frame select File|New Frame".

Если так и поступить, то в вашем проекте появится новый модуль и новый кадр — на первый взгляд похожий на форму.

Теперь вы можете наполнять созданный кадр компонентами. Хороший пример использования кадров приводится вместе с Delphi — это приложение Frames Demo в папке DEMOS\FRAMES\DB. Зачем там кадры? Для доступа и визуализации данных одной таблицы БД требуются компоненты доступа к данным TTable и TDataSource, компонент отображения данных TDBGrid и компонент управления TDBNavigator. Если в базе есть поля типа

Memo И Graphic, нужно добавить еще TDBMemo И TDBImage. Так и поступили разработчики примера. От одного базового кадра для работы с данными порождены еще несколько — для решения специфических задач.

Итак, кадр создан. Что дальше? В его всплывающем меню есть пункт "Add to Palette". При его вызове возникает диалоговое окно.

Вы можете задать имя нового кадра, его будущую страницу Палитры (по умолчанию это страница Templates) и значок. Специфика его использования в том, что все изменения в кадре будут немедленно отображаться в использующих его проектах. Если вы захотите использовать кадр, он появится в вашем проекте не в том состоянии, в котором находился в момент помещения в Палитру, а в своем текущем состоянии.

Класс TApplication

На уровне методов и свойств этого компонента осуществляется управление выполняемым приложением. Он отсутствует в Палитре компонентов, но доступ к его свойствам в визуальной среде разработки все-таки присутствует. Часть свойств доступна через диалоговое окно свойств проекта. Все методы обработчики событий доступны как методы компонента TApplicationEvents, впервые появившегося в Delphi 5.

Компонент TApplicationEvents — один из самых простых. Он не имеет никаких опубликованных свойств, кроме Name и Tag (как и положено прямому потомку TComponent). Но зато связанные с ним события — это события, которые возникают в объекте Application. Описывая обработчики этих событий, вы на самом деле описываете поведение Application.

В каждом проекте экземпляр класса приложения доступен для использования через переменную

var Application: TApplication;

Пример использования этой переменной имеется в каждом проекте приложения. В файле проекта стандартного приложения переменная Application используется для вызова следующих методов.

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

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

К информационным могут быть отнесены следующие свойства:

Теперь рассмотрим методы и свойства в последовательности, соответствующей "жизненному циклу" приложения. При запуске на выполнение Application сначала создает и инициализирует формы приложения при помощи метода:

procedure CreateForm (FormClass: TFormClass; var Reference);

Параметр Reference будет указывать на созданный экземпляр формы класса

FormClass.

С обращений к этому методу начинается код, содержащийся в файле с расширением .dpr; при этом в нем создаются только те формы, которые содержатся в списке Auto-create forms в параметрах проекта. Метод работает таким образом, что в случае, если указатель на главную форму в данный момент равен Nil, то созданная форма становится главной. Из этого следует, что главной всегда будет первая из создаваемых форм. Указатель на нее описан в свойстве:

property MainForm: TForm;

Здесь нужно сделать специальную оговорку. Помимо главной и других форм, у приложения есть еще одно — невидимое — окно. Именно оно является по-настоящему главным и именно в нем обрабатываются все сообщения Windows, управляющие состоянием приложения. Его дескриптор доступен в свойстве:

sroperty Handle: HWnd;

После создания всех форм метод

procedure Run;

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

FMainForm.Visible := True;

repeat

HandleMessage until Terminated;

Вызываемый в нем метод

procedure HandleMessage;

извлекает следующее сообщение из очереди и передает его на обработку следующим образом:

procedure TApplication.HandleMessage;

var

Msg: TMsg;

begin

if not ProcessMessage(Msg) then Idle(Msg);

end;

Метод ProcessMessage ищет нужный обработчик сообщения и передает ему управление. Назначение метода idle поясняется ниже.

О поступлении сообщения оповещает вызов обработчика события:

property OnMessage: TMessageEvent;

TMessageEvent = procedure (var Msg: TMsg; var Handled: Boolean) of object;

Используя обработку OnMessage, программист может переопределить обработку любого (или любых) сообщений (кроме wm_quit и сообщений системы оперативной подсказки). Для этого, выполнив внутри метода необходимые действия, в параметре Handled нужно вернуть True. Тогда сообщение не поступит в функцию окна приложения. Необходимо быть крайне осторожным, подменяя системные обработчики сообщений.

Существует другой способ перехвата сообщений. Для этого нужно, написать метод, имеющий следующий тип

TWindowHook = function (var Message: TMessage): Boolean of object;

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

procedure HookMainWindow(Hook: TWindowHook);

Если одна из процедур-перехватчиков типа TWindowHook в цепочке вернет True, это будет означать, что сообщение обработано.

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

procedure UnhookMainWindowfHook: TWindowHook);

Если сообщений в очереди нет, то может быть выполнена "фоновая" работа, предусмотренная программистом. Для этого нужен обработчик события, которое генерируется в этой ситуации внутри метода idle в HandleMessage:

property Onldle: TIdleEvent;

TIdleEvent = procedure (Sender: TObject; var Done: Boolean) of object;

Обработчик должен вернуть признак потребности в последующих событиях Onldle в булевом параметре Done. Если "все сделано" и он присваивает Done значение True, то приложение переходит в режим ожидания сообщения и обработчик не вызывается до возникновения следующей ситуации отсутствия сообщений в очереди. Если возвращается значение False, метод будет вызываться все время при отсутствии сообщений.

Как видно из приведенного кода метода Run, сообщения обрабатываются вплоть до установки флага:

property Terminated: Boolean;

Такой флаг устанавливается при получении сообщения wm_quit. Используя его, можно вставить свои действия на завершающем этапе (после окончания обработки сообщений перед вызовом деструктора Application). Свойство это доступно только для чтения: поэтому завершить приложение можно, вызвав метод:

procedure Terminate;

которая и посылает сообщение wm_quit.

Одна или более форм приложения могут иметь статус fsStayOnTop, то есть постоянно находятся поверх других форм (не имеющих этого статуса). Для того чтобы на время отключить его действие, применяется пара методов:

Эти методы могут быть полезными, когда необходимо вывести информацию поверх формы со статусом fsStayonTop. В самом объекте Application они вызываются соответственно при деактивизации/активизации приложения, сворачивании/разворачивании главной формы.

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

property OnActivate: TNotifyEvent;

property OnDeactivate: TNotifyEvent;

Текущее состояние активности можно узнать в свойстве:

property Active: Boolean;

События, возникающие соответственно при сворачивании и восстановлении главной формы приложения, также относятся к объекту Application:

property OnMinimize: TNotifyEvent;

property OnRestore: TNotifyEvent;

Для программного выполнения таких операций есть два метода:

procedure Minimize;

procedure Restore;

У класса TApplication есть метод

procedure BringToFront;

У формы также есть метод с этим названием, который показывает форму поверх остальных и активизирует ее. Отличие этих методов в том, что Form.BringToFront активизирует вызвавшую его форму, а метод Application.BringToFront — ту форму, которая была активна последней.

Во время выполнения приложения могут возникать исключительные ситуации (см. гл. 4). При их возникновении обработку осуществляет метод:

procedure HandleException(Sender: TObject);

Стандартная обработка подразумевает вызов метода:

procedure ShowException(E: Exception);

который отображает диалоговое окно с именем приложения в заголовке и сообщением об ошибке (содержащемся в параметре — объекте Е класса Exception).

Стандартную обработку можно перекрыть (во всех случаях, кроме обработки исключительной ситуации EAbort), определив обработчик события:

property OnException: TExceptionEvent;

TExceptionEvent = procedure (Sender: TObject;

E: Exception) of object;

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

property HelpFile: string;

Если оно определено, то вызов помощи можно осуществить тремя видами запросов, которым соответствуют методы:

Все функции возвращают True, если файл Help определен, и произошел вызов справочной системы.

Если вы хотите перехватить обращение к справочной системе, то необходимо написать обработчик события:

property OnHelp: THelpEvent;

THelpEvent = function(Command: Word; Data: Longint; var CallHelp: Boolean) :Boolean of object;

В нем вы можете предусмотреть предоставление собственной помощи пользователю. Результат функции равный True означает успех (помощь предоставлена). Если после обработки события по-прежнему нужно вызвать систему помощи Windows (через winHelp), то в параметре CallHelp нужно вернуть True.

Метод:

function MessageBox(Text, Caption: PChar; Flags: Word): Integer;

содержит вызов стандартного диалогового окна с тем же названием и назначением.

Если вы хотите использовать в составе Delphi и вызывать из форм приложения диалог, созданный с помощью других средств программирования, то дескриптор такого диалога должен быть присвоен свойству:

property DialogHandle: HWnd;

Подробные правила работы с этим свойством описаны в документации разработчика компонентов Delphi.

Наконец, упомянем о системе оперативной подсказки. У приложения имеется собственный текст подсказки, определяемый свойством:

property Hint: siring;

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

Целый ряд методов и свойств объекта, отвечающих за реализацию системы оперативной подсказки, подробно описан в главе 12. Поэтому они здесь только перечисляются:

property ShowHint: Boolean;

property HintPause: Integer-property HintHidePause: Integer-property HintShortCuts: Boolean-property HintShortPause: Integer-property OnHint: TNotifyEvent;

property OnShowHint: TShowHintEvent;

property HintColor: TColor;

procedure CancelHint;

Резюме

Формы являются основой пользовательского интерфейса любого приложения. Класс TForm, инкапсулирующий форму, обладает богатыми возможностями по управлению компонентами, размещенными на ней.

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

Диалоги представляют собой формы, оптимизированные для решения специальных задач. В VCL Delphi имеется целый ряд компонентов стандартных системных диалогов.

Материал этой главы будет полезен при изучении следующих глав: