Чаще всего Delphi используется для создания приложений. Вы можете создавать приложения любого типа — от утилит командной строки до программы электронной почты или многопользовательской финансовой базы данных.
В этом пункте Вы познакомитесь с созданием приложений, и с тремя основными типами приложений. В конце будет приведено несколько советов о том, как ускорить создание программ и сделать их более профессиональными.
Подобно фундаменту здания, форма представляет собой фундамент программы, на котором строится все остальное. Форма — это место, где пользователь общается с программой. Приложение может иметь несколько форм, каждая из которых выполняет свое особое предназначение.
Delphi инкапсулирует концепцию форм в класс TForm, и каждая форма, создаваемая с помощью конструктора форм (Form Designer), наследует основные свойства, методы и события этого класса.
Класс TForm не является прямым потомком TWinControl.
Свойства TForm
Класс TForm предоставляет возможность изменять его поведение и внешний вид формы с помощью ряда свойств.
Active
Свойство Active определяет, имеет ли форма фокус ввода. Если имеет, оно возвращает True, если нет False. Windows выводит заголовок активной формы цветом, отличающимся от цвета неактивных. Совет: Неактивные окна продолжают получать сообщения о перемещении и выборе мыши. Независимо от типа приложения в один момент времени может быть активной только одна форма. Вы можете обратить внимание на то, что заголовок родительской формы в MDI-приложении изображен „активным“ цветом. Не попадитесь на эту удочку — свойство Active родительского MDI-окна, никогда не бывает равным True.
Совет: Свойство activeform класса TScreen, возвращает форму, которая в данный момент имеет фокус ввода.
ActiveControl
Свойство ActiveControl указывает на потомка TWinControl, имеющего в настоящий момент фокус ввода. Вы можете установить значение во время создания программы, определив, какой элемент будет иметь фокус ввода при инициализации формы. Назначение ActiveControl во время работы программы — установка фокуса ввода в поле с некорректно введенными данными. Приведенный ниже фрагмент кода позволяет проверить текст, введенный в элемент editCustName, перед закрытием формы.
procedure TDataEntryForm.FormCloseQuery(Sender: Tobject);
var CanClose: Boolean);
begin
{Проверяем, введен ли текст в элемент.}
if edtCustName.Text = ' ' then
begin
{Запрещаем закрытие.}
CanClose:= False;
(Устанавливаем фокус в поле с некорректными данными.)
ActiveControl:= editCustName;
end;
end;
Совет: Метод setfocus потомков TWinControl устанавливает фокус ввода и обновляет свойство ActiveControl. Большинство событий передает параметр Sender своему обработчику. Sender определяет, какой элемент обнаружил событие и запустил обработчика.
AutoScroll, HorzScrollBar и VertScrollBar
Свойство AutoScroll управляет появлением полос прокрутки в форме, размеры которой не позволяют вывести все ее элементы одновременно. Если свойство равно True, и вы изменили размеры так, что не все элементы формы видны, в форме автоматически появляются полосы прокрутки. Если же значение свойства— False, вы теряете доступ к элементам формы, не поместившимся на экране.
Совет: Компонентtscrollbar, позволяет прокручивать форму независимо от свойства AutoScroll.
Вы можете управлять полосами прокрутки с помощью свойств HorzScrollBar и VertScrollBar. Это весьма полезно в программах, реализующих свойство распахивания окна. Поскольку размеры выводимой диаграммы могут быть больше размеров формы, а выводите вы ее самостоятельно, AutoScroll не активизируется, и вам следует обеспечить прокрутку и рисование, самому. Вот пример такого кода, обеспечивающего прокрутку в двойном размере формы.
{Устанавливаем диапазон вертикальной прокрутки.}
VetrScrollBar.Range:= Height * 2;
{Показываем вертикальную полосу прокрутки.}
VertScrollBar.Visible:= True;
{Устанавливаем диапазон горизонтальной прокрутки.}
HorzScrollBar.Range:= Width * 2;
{Показываем горизонтальную полосу прокрутки.}
HorzScrollBar.Visible:= True;
BorderIcons
Свойство Borderlcons представляет собой набор логических значений, использующийся для определения набора пиктограмм в заголовке формы.
Значения biMinimize и biMaximize создают пиктограммы, которые позволяют свернуть и развернуть форму с помощью мыши.
Совет: Для того чтобы значения biMinimize и biMaximize работали, необходимо установить свойство borderstyle равным bsSizeable или bsSizeToolWin.
Значение biHelp выводит кнопку с вопросительным знаком. Щелчок на ней вызывает контекстно-зависимую справку, которая выводится, как текст подсказки Hint, т. е. без вызова Windows Help. На рис. 1.1 представлена такая кнопка.
Рис. 1.1.Доступ к контекстно-зависимой справке можно получить с помощью щелчка на кнопке с вопросительным знаком в заголовке формы.
Значение biSystemMenu создает слева от заголовка пиктограмму, позволяющую вызывать системное меню, как показано на рис. 1.2.
Рис. 1.2.Системное меню позволяет перемещать и закрывать форму, а также изменять ее размеры.
Совет: Для того чтобы значения biMinimize, biMaximize и biHelp работали, необходимо присвоить свойству заметку bordericons значение bisystemmenu.
BorderStyle
Свойство BorderStyle перечислимого типа позволяет определить:
вид заголовка формы
доступные кнопки в заголовке формы
отображение строки меню
поведение границ формы
На рис. 1.3 показана форма для шести значений BorderStyle. Каждая форма создавалась как форма размером 200х200 пикселей.
Рис. 1.3.Влияние значения BorderStyle на вид формы. Значения таковы: bsSizeable, bsDialog, bsSingle, bsSizeToolWin,bsToolWindow и bsNone
По умолчанию свойство BorderStyle имеет значение bsSizeable, создающее обычное окно с изменяемыми размерами. Такое окно имеет стандартную строку заголовка и не имеет ограничений на расположение в ней кнопок. Примеры таких окон — программы Explorer и Notepad.
Значение bsDialog создает диалоговое окно, которое используется, когда программа требует от вас ответа для продолжения выполнения программы или для вывода информации, 16-разрядные версии Windows выводили такое окно как окно с широкой границей того же цвета, что и заголовок. Сейчас, если вы будете следовать новому трехмерному интерфейсу, границы окна будут выглядеть так же, как границы обычного окна! Видимое различие между стандартными и диалоговыми окнами в связи с тем, что последнее не может изменять размеры, состоит лишь в том, что указатель мыши не изменяется при пересечении рамки окна.
Совет: Значения biminimize и biMaximize свойства Borderlcons не будут работать, если свойство BorderStyle установлено равным bsDialog.
Третий по популярности стиль окон — bsSingle, создающий форму, которая не может изменять размеры во время работы. В отличие от bsDialog, bsSingle не запрещает установку любых пиктограмм. Единственное ограничение состоит в том, что кнопка сворачивания окна, будучи выведенной, является недоступной (блокированной). Пример такой программы — Calculator. На рис. 1.4 вы видите другой пример окна bsSingle.
Рис. 1.4.Стиль bssingle полезен, когда пользователю не надо изменять размер окна.
Панель инструментов (Toolbar) позволяет быстро получить доступ к сгруппированным функциям. В Delphi можно сконструировать панель инструментов, поместив группу компонентов TSpeedButton в форму, имеющую стиль bsSizeToolWin или bsToolWindow. Окно в стиле bsSizeToolWin может изменять размеры и не имеет кнопок biMinimize, biMaximize HbiHelp. Окно в стиле bsToolWindow действует так же, но не позволяет изменять размеры.
Стиль bsNone создает окно без рамки и заголовка. Такое окно не рекомендуется использовать в качестве стандартного или диалогового, однако оно может быть полезным в программах сохранения экрана или заставках.
Совет: Если вы выбрали для свойства BorderStyle значение, создающее окно с разрешенным изменением размеров, Delphi автоматически установит значение AutoScroll равным True. Формы со стилями bsDialog и bsNone не могут иметь строки меню.
Height и Width
Эти свойства определяют высоту и ширину формы в пикселях и обычно используются для изменения размеров формы во время работы на дисплеях разной разрешающей способности. Вот пример увеличения размеров формы до размеров всего экрана.
{Перемещаем форму в верхний левый угол экрана.}
Left:= 0;
Тор:= 0;
(Изменяем размеры формы.)
Width:= Screen.Width;
Height:= Screen.Height;
(Класс TScreen, о котором будет сказано ниже, и его экземпляр Screen предоставляют доступ к информации о размерах всего экрана.)
Приведенный код, конечно, работает, но плохо, так как требуется четыре обновления формы. На самом деле лучше использовать метод SetBounds, определенный у потомков TWinControl:
SetBounds(0, 0, Screen.Width, Screen.Height);
ClientHeight и Clientwidth
Окно состоит из двух частей — клиентской и не клиентской. Обычно приложение выводит изображения только в клиентской области, размер которой возвращается через свойства ClientHeight и ClientWidth. Обычно эти свойства используются для того, чтобы убедиться, что в форме может выводиться весь объект определенного размера. Показанный ниже текст приводит размер клиентской области формы в соответствие размерам изображения, содержащегося в компоненте TImage, ImgPicture.
with imgPicture.Picture do
begin
(Изменение размера.)
ClientWidth:= Width;
ClientHeight:= Height;
end;
Не клиентская область обычно прорисовывается Windows и включает строку заголовка, меню и рамку окна. Вы можете рисовать в этой части окна, перехватив сообщение WMNCPAINT.
FormStyle
Свойство FormStyle перечислимого типа определяет, как форма взаимодействует с вашим приложением и Windows.
Существует два основных стиля форм — MDI (Multiple Document Interface — многодокументный интерфейс) и не MDI. Имеется два MDI-стиля (fsMDIForm и fsMDIChild), которые рассматриваются ниже. Не MDI формы существуют также в двух вариантах— fsNormal и fsStayOnTop. Наиболее популярен стиль fsNormal, который создает стандартный стиль, используемый для диалогов, панелей инструментов и SDI-приложений.
Стиль fsStayOnTop применяется реже и создает форму, всегда остающуюся поверх других форм и приложений, что может быть полезно при выводе системной информации и использовании ресурсов. Примером такого окна является окно программа Chat, используемой при работе в сети.
Вот как можно реализовать, подобно программе Chat, установку вывода поверх других окон путем выбора пункта меню.
procedure TFormI.mnuAlwaysOnTopClick(Sender: TObject);
begin
with mnuAlwaysOnTop do
begin
{Переключаем отметку выбора пункта меню.}
Checked:= not Checked;
{Проверка установок меню.}
if Checked then
(Устанавливаем стиль fsStayOnTop.)
FormStyle:= fsStayOnTop
else
{Возвращаем нормальный стиль.}
FormStyle:= fsNormal;
end;
end;
Совет: Изменение свойства FormStyle вызывает событие OnShow.
Icon
Свойство Icon определяет пиктограмму, выводимую Windows при сворачивании вашей формы. В интерфейсе Windows 95 эта пиктограмма также выводится в левом верхнем углу формы на кнопке системного меню. Если вы не определите значения для этого свойства, будет использоваться свойство Icon глобального объекта Application.
KeyPreview
Объект TForm наследует от класса TWinControl обработку событий OnKeyDown, OnKeyUp и OnKeyPress, и свойство KeyPreview определяет ситуации, в которых эти сообщения запускаются. Если значение KeyPreview равно False, события клавиатуры пересылаются только тому управляющему элементу, который имеет фокус ввода. В неординарном случае, когда в форме нет управляющих элементов, событие передается форме. Если же значение свойства установлено равным True, событие сначала пересылается форме, а затем — управляющему элементу, имеющему фокус ввода.
Совет: Поскольку клавиша<tab> используется для передачи фокуса другому управляющему элементу, она не вызывает генерации события.
Обычно свойство используется KeyPreview для обработки функциональных клавиш, которые должны быть переданы форме независимо от текущего активного элемента.
Без этого свойства обработка функциональных клавиш сводится к написанию для каждого элемента дополнительного обработчика, который должен отслеживать нажатие функциональных клавиш. Более элегантным является использование разделяемого обработчика событий, так что лучше всего применять свойство KeyPressed.
При этом все нажатия клавиш отсылаются обработчикам событий OnKeyDown, OnKeyUp и OnKeyPress автоматически и для „отлова“ функциональной клавиши надо написать только один обработчик события OnKeyDown формы. Вот пример закрытия формы при нажатии клавиши procedure TFormI.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState): begin {Проверить, нажата ли клавиша if Key = VKF2 then {Закрыть форму.} Close; end; Рассматривая принцип работы этого кода, имейте в виду, что события OnKeyDown и OnKeyUp используют виртуальные коды (все они перечислены в файле WINDOWS.PAS). Menu Это свойство определяет компонент TMainMenu, который предоставляет главное меню формы. Свойство Menu позволяет сделать меню контекстно-зависимым и часто используется в OLE-приложениях при замене вашего исходного меню на меню приложения-сервера. На рис. 1.5 показан документ Wordpad с внедренным объектом MediaClip. Обратите внимание, что меню и панель инструментов при этом те же, что и у Media Player. Рис. 1.5.Вы можете создать контекстно-зависимое меню с помощью свойства Menu Для изменения свойства Menu просто присвойте ему новое значение (TMainMenu или его потомок): Menu:= mnuMainMenu; Position Position — это свойство перечислимого типа, определяющее размещение формы при запуске приложения. Значение по умолчанию (poDesigned) заставляет форму выводиться в месте, определенном при разработке приложения. Положение и размер формы при этом берутся из свойств Left, Top, Height и Width. Поскольку вы не можете знать заранее, в какой системе будет запущено ваше приложение, может оказаться, что на мониторе с низким разрешением при использовании этого значения свойства будет видна только часть формы. Более полезно значение poScreenCenter, использующее заданные вами при создании приложения значения Width и Height, но оно так изменяет Left и Тор, что форма выводится в центре экрана. Если вы установите Position равным poDefault, Windows автоматически установит размеры и положение формы, но вы при этом лишитесь возможности контролировать ее размеры. Можете создать форму размером 200х200, которая будет выведена как 640х480. Из-за этого, в частности, не допускается применение данного значения для MDI-форм. Значение poDefaultPosOnly более полезно, так как оно автоматически определяет расположение формы, но не ее размеры (а потому рекомендуется для MDI-форм, в которых требуются определенные размеры дочерних форм). Последнее значение свойства (poDefaultSizeOnly) автоматически определяет размер, но не расположение формы. Это значение может использоваться там, где важно положение формы на экране, а не ее размер. Хотя свойство Position позволяет определить, каким образом будет выводиться форма, профессионально сделанные приложения сами запоминают свое расположение на экране и при следующем запуске выводятся в той же позиции и с тем же размером. Это осуществимо, например, благодаря записи положения окна в Registry или в INI-файле, в том же каталоге, где находится приложение. Обычно сохранение позиции и размеров экрана выполняется в самый последний момент — при уничтожении формы. procedure TForml.FormDestroy(Sender: TObject); var sAppPath: String; iniSettings: TINIFile; begin {Получить путь к ЕХЕ-файлу приложения.} sAppPath:= ExtractFilePath(Application.EXEName); {Создаем объект TINIFile.} iniSettings:= TINIFile.Create(sAppPath + 'SETTINGS.INI'); try {Записываем свойства в INI-файл.} iniSettings.Writelnteger(Name,'Left',Left); iniSettings.Writelnteger(Name,'Top',Top); iniSettings.Writelnteger(Name,'Width',Width); iniSettings.Writelnteger(Name,'Height',Height); finally iniSettings.Free; end; end; Совет: Для компиляции примера не забудьте включить в раздел uses модуль INIFiles. После выполнения кода ваш INI-файл будет содержать нечто вроде [Form1] Left=108 Тор=174 Width=540 Height=165 Заметьте, что для строки раздела используется свойство Name вашей формы. Восстановить положение формы на экране немного сложнее (главным образом из-за того, что следует отработать ситуацию, когда INI-файла нет). procedure TFormI.Create(Sender: TObject); const CNOTFOUND = –1; var sAppPath: String; iniSettings: TINIFile; liValue: Longint; begin {Получаем путь к ЕХЕ-файлу приложения.} sAppPath:= ExtractFilePath(Application.ExeName); {Создаем объект TINIFile.} iniSettings:= TINIFile.Create(sAppPath + 'SETTINGS.INI'); try {Пытаемся считать значение Left.} liValue:= iniSettings.Readlnteger(Name,'Left',cNOTFOUND); {Проверяем, считано ли значение.} if liValue = cNOTFOUND then begin {Свойства в INI-файле не найдены — центруем форму.} Left:= (Screen.Width — Width) div 2; Top:= (Screen.Height — Height) div 2; end else begin {Считываем значения из INI-файла.} Left:= iniSettings.Readlnteger(Name,'Left',Left); Top:= iniSettings.Readlnteger(Name,'Top',Top); Height:= iniSettings.Readlnteger(Name,'Height',Height); Width:= iniSettings.Readlnteger(Name,'Width'.Width); end; finally iniSettings.Free; end: end; WindowState Свойство перечислимого типа WindowState определяет состояние окна — свернутое, развернутое или нормальное. По умолчанию оно имеет значение wsNormal (при этом окно выводится в состоянии, определяемом свойствами Position, Left, Top, Height и Width). Чтобы свернуть или развернуть форму, используются значения wsMinimize и wsMaximize. События TForm Класс ТForm добавляет несколько событий к родительскому классу TWinControl. Эти события позволяют изменять поведение формы путем выполнения загрузки и сохранения информации о состоянии формы или распределения и освобождения дополнительных ресурсов. Когда форма создается и отображается, происходит пять следующих событий. 1. OnCreate запускается при создании формы и позволяет распределять ресурсы и инициализировать форму. 2. OnShow происходит непосредственно перед выводом формы на экран. К этому времени все элементы управления и компоненты созданы и инициализированы. Совет: Хотя к тому моменту, когда происходит событие OnShow, форма еще не видна, свойство Visible установлено равным True. 3. OnResize генерируется при изменении размера формы во время выполнения приложения. Обычно здесь помещается код для изменения размера и положения на экране элементов управления, не поддерживающих свойство Align. Событие OnResize также однократно генерируется при создании формы, когда Delphi устанавливает начальные размеры формы. Совет: Onresize вызывается неоднократно в процессе изменения размеров формы. 4. OnActivate происходит при получении формой фокуса ввода. OnActivate вызывается только при переходе фокуса ввода от одной формы к другой в пределах одного приложения. При переключении между приложениями Delphi генерирует событие OnActivate глобального объекта Application. 5. OnPaint запускается, когда необходимо перерисовать форму. Это может происходить, когда форма только что стала видимой, при частичном удалении перекрывающих ее элементов или увеличении размеров. Событие полезно, если вы перерисовываете какую-то часть формы самостоятельно. Совет: Событие oncreate происходит один раз за все время существования формы, прочие же события могут вызываться неоднократно. При закрытии и уничтожении формы также генерируется пять следующих событии. 1. OnCloseQuery генерируется в ответ на действия, закрывающие форму. Обработчик получает логическую переменную CanClose, определяющую, может ли форма быть закрыта. По умолчанию она имеет значение True, но если вы в обработчике установите False, форма останется открытой. Обычно это используется для сохранения не сохраненных файлов или для подтверждения закрытия формы. Вот пример такого кода. procedure TFormI.FormCloseQuery(Sender: TObject; var CanClose: Boolean); begin CanClose:= MessageDIg('Close form?', mtConfirmation, [mbYes,mbNo], 0) = mrYes; end; 2. OnClose генерируется непосредственно перед закрытием формы. Обычно оно используется для изменения стандартного поведения формы при закрытии. Для этого Delphi передает в обработчик события переменную Action, которая может принимать одно из четырех значений: caHide, caMinimize, caNone или caFree. По умолчанию для не MDI-форм используется caHide, скрывающее форму. Для дочерних MDI-форм значение по умолчанию, сворачивающее форму, равно caMinimize. Если Action установлено равным caNone, закрытия не происходит. caFree заставляет Delphi закрыть форму и освободить всю связанную с ней память. Если после этого сослаться на объект формы, произойдет исключительная ситуация. Совет: onclose вызывается только при закрытии формы с помощью щелчка на кнопке закрытия или вызова функции Close. Если вы закрываете главную форму приложения, все другие открытые формы закрываются без вызова события OnClose. Событие OnCloseQuery вызывается всегда, независимо от способа закрытия формы. 3. OnDeActivate происходит при потере формой фокуса ввода. Запуск происходит по тем же правилам, что и запуск события OnActivate. 4. OnHide запускается непосредственно перед тем, как форма станет невидимой. Совет: Хотя при вызове onhide форма еще видна, ее свойство Visible установлено равным False. 5. OnDestroy генерируется непосредственно перед уничтожением формы. Обычно оно используется для освобождения ресурсов, выделенных в OnCreate. Совет: Событие ondestroy вызывается только один раз за все время существования формы, прочие события могут вызываться неоднократно. Повторное использование форм К этому моменту вы уже должны быть хорошо знакомы с объектно-ориентированной природой Delphi. Поскольку TForm представляет собой класс, он может повторно использоваться, расширяться и изменяться. Повторное применение форм поддерживается через шаблоны форм и наследование. Оба эти метода используют Object Repository Шаблоны форм Шаблоны форм (Form Templates) предоставляют основу для новой формы. По одной заготовке можно создать несколько форм. В Delphi есть хранилище объектов (Object Repository), в котором содержится множество различных шаблонов форм, диалогов и проектов. Использование шаблонов форм Приведенные ниже опции позволяют использовать шаблоны форм. Copy. Эта опция добавляет копию шаблона формы в ваш проект. Изменения объекта в проекте не влияют на другие объекты. Use. Эта опция связывает шаблон непосредственно с вашим проектом. Изменения в проекте воздействуют на объект, находящийся в хранилище, и наоборот. Для иллюстрации сказанного добавим в проект новую форму, основанную на шаблоне About box, следующим образом. Выберите команду File/New Application. Появится пустое приложение. Выберите команду File/New. Появится диалоговое окно New Items. Щелкните на вкладке Forms вверху диалогового окна. Delphi выведет доступные шаблоны форм, как показано на рис. 1.6. Убедитесь, что выбран метод Copy или Use. Щелкните на кнопке ОК. Новая форма About box будет добавлена в ваш проект. Если вы выбрали опцию Copy, новая форма будет дубликатом шаблона и дальнейшая работа с ней не отразится на шаблоне-оригинале. При использовании Use все изменения в одном проекте через хранилище форм будут переданы во все проекты, применяющие эту форму с опцией Use. Добавление собственного шаблона Хотя имеющиеся шаблоны весьма полезны и хорошо сделаны, для продолжительной профессиональной работы их будет недостаточно. Наверняка нужно будет что-либо доработать или создать новую форму, которую можно было бы использовать в других приложениях. Чтобы не делать одну и ту же работу дважды (трижды, восемьюжды…), создайте шаблон формы и поместите его в хранилище объектов. Создайте форму, добавьте в нее компоненты и приправьте все это кодом по вашему вкусу. Конечно, вы вполне можете использовать в качестве заготовки имеющийся шаблон. Сохраните форму в папке OBJREPOS Delphi.Совет: object repository не хранит копий всех шаблонов, а устанавливает связи с их DFM— и PAS-файлами. Если вы удалите такой файл, шаблон станет недееспособным. Щелкните правой кнопкой мыши на форме и затем выберите команду Add to Repository. При этом Delphi выведет диалоговое окно Add To Repository, показанное на рис. 1.7. Рис. 1.6. Шаблоны форм позволяют использовать заготовленные общие формы Рис. 1.7.Используйте диалоговое окно Add To Repository для добавления ваших собственных шаблонов в хранилище объектов Из списка Forms выберите форму, которую нужно добавить в хранилище. Введите заголовок формы в поле Title (он будет использоваться в качестве подписи под пиктограммой в диалоговом окне New Items). Из раскрывающегося списка Page выберите страницу, на которой будет размещен шаблон. Щелкните на кнопке ОК, и форма будет добавлена в Object Repository. Совет: Delphi автоматически включает вновь созданные формы в текущий проект. Они связаны с проектом, как будто вы воспользовались опцией Use. Разделяемое хранилище Вам может не понравиться то, что ваши шаблоны содержатся в хранилище Delphi. Предположим, что вы де инсталлируете Delphi (при этом, чтобы сохранить свои шаблоны, вы будете вынуждены вручную сохранять их в другом месте, а потом вновь добавлять их в очередное хранилище). Или, например, вы работаете в сетевом окружении и хотите поделиться своими формами с другими программистами. Обе эти проблемы позволяет решить концепция разделяемого хранилища. Разделяемое хранилище (shared repository) представляет собой папку с двумя файлами, используемыми для хранения информации о расположении шаблонов. Вы можете определить расположение разделяемого хранилища, выполнив следующие действия. Выберите команду Tools/Options, и на экран будет выведено диалоговое окно Environment Options, показанное на рис. 1.8. Щелкните на вкладке Preferences вверху диалогового окна. В поле ввода Directory введите путь к папке, которую вы хотите использовать в качестве разделяемого хранилища. Щелкните на ОК. Теперь при выводе диалогового окна New Items Delphi будет просматривать эту папку в поисках шаблонов. После того как разделяемое хранилище определено, вы вольны в выборе места хранения своих шаблонов. Управление хранилищем объектов После добавления шаблона в хранилище вы вдруг обнаруживаете, что совсем забыли внести в него последние исправления. Вы можете сделать это, используя диалоговое окно Object Repository, показанное на рис. 1.9. Для его вызова воспользуйтесь командой Tools/Repository. В списке Pages, расположенном слева в диалоговом окне, перечислены страницы, выводимые в диалоговом окне New Items. Управлять этим набором можно с помощью кнопок Add Page, Delete Page и Rename Page. Рис. 1.8.Опция shared Repository позволяет определить разделяемое хранилище шаблонов Рис. 1.9.Использование диалогового окна Object Repository для работы с шаблонами Выбор страницы из списка приводит к заполнению списка Objects объектами, содержащимися на этой странице. Если вы выберете элемент [Object Repository], будут показаны все объекты в хранилище. Вы можете перемещать объекты путем их перетаскивания из списка Objects на нужную страницу в списке Pages. Кнопка Edit Object позволяет вывести диалоговое окно Edit Object Info, показанное на рис. 1.10. С его помощью можно редактировать свойства объекта в любой момент. Кнопка Delete Object удаляет объект из хранилища, но не удаляет его файлы с диска. Переключатели, расположенные внизу диалогового окна, выполняют две задачи. Первый переключатель. New Form, позволяет определить шаблон, используемый при создании новой формы. Второй, Main Form, определяет шаблон, используемый для построения главной формы при создании нового приложения. Для назначения соответствующих шаблонов просто выберите объект из списка Objects и отметьте нужную опцию. Рис. 1.10. Редактировать свойства объекта можно в хранилище с помощью диалогового окна Edit Object Info Если вы выбираете проект, а не форму, диалоговое окно заменит переключатели новыми переключателями New Project. Он позволяет определить шаблон проекта, используемый при создании нового приложения. Наследование форм Наследование форм воплощает лучшие возможности повторного использования форм Use и Copy. При копировании вы создаете дубликат формы и добавляете в него необходимые компоненты и код. Неудобство этого метода состоит в том, что изменения не вносятся в шаблон. При использовании Use изменения вносятся не только в шаблон, но и во все объекты в других проектах. Наследование позволяет создать множество экземпляров шаблона, которые могут отличаться один от другого, как при использовании Copy. Оно так же автоматически вносит изменения в объект хранилища, как и при использовании Use. Использование наследования форм В хранилище объектов содержится несколько примеров наследования форм. Для того чтобы наследовать одну форму из другой, выполните следующие действия. Выберите команду File/New Application. При этом появится пустое приложение. Выберите команду File/New, и будет выведено диалоговое окно New Items. Щелкните на вкладке Dialogs, и будет выведена страница диалогов. Выберите диалог с пиктограммой Help, кнопки которого выровнены вертикально по правой стороне формы. Выберите опцию Inherit. Щелкните на кнопке ОК, и Delphi выведет новую диалоговую форму. Заголовок нового диалогового окна— OKHelpRightDlg2. Почему Delphi создает это диалоговое окно как OKHelpRightDIg? Ответ заключается в наследовании форм. В списке форм вашего проекта содержится четыре объекта— Form1, OKHelpRightDIg, OKHelpRightDlg2 и OKRightDlg. Поскольку вы наследуете новую форму из OKHelpRightDIg, Delphi включает его в ваш проект для компиляции вашей программы. Это и приводит к наименованию новой формы как OKHelpRightDlg2. К тому же это позволяет избежать конфликта имен. В свою очередь, OKHelpRightDIg — наследник OKRightDlg, а потому последний также включен в проект. ПРЕДОСТЕРЕЖЕНИЕ: Две родительские формы, OKHelpRightDIg и OKRightDlg, связываются с проектом так же, как и опция Use, поэтому, решив их изменить, вы измените объекты, хранящиеся в Object Repository. Цепочка наследования отражена в автоматически генерируемом коде. Описание класса OKHelpRightDlg2 выглядит так. TOKHelpRightDlg2 = class (TOKHelpRightDIg) private {Закрытые объявления.} public {Открытые объявления.} end; Совет: Описание класса или типа начинается с буквы Т. Это объявление не назовешь чересчур информативным. Поскольку вы не добавляли ни новых компонентов, ни кода, OKHelpRightDlg2 не определяет ни новых свойств, ни методов или событий. Определение класса TOKHelpRightDIg несколько интереснее. TOKHelpRightDIg = class (TOKRightDIg) HelpBtn: TButton; procedure HelpBtnClick(Sender: TObject); private {Закрытые объявления.} public {Открытые объявления.} end; Как вы можете видеть, OKHelpRightDIg получает компоненты и код от OKRightDIg и добавляет объект HelpBtn типа TButton, а также обработчик события HelpBtnClick. Преимущества наследования форм Как уже упоминалось ранее, преимущества наследования форм заключаются в возможности добавления новых компонентов и кода в объект и наличии связи с оригиналом шаблона в хранилище. Если вы изменяете шаблон, наследуемый объект будет автоматически обновлен. Выполните следующие действия. Выберите команду File/New Application, и появится пустое приложение. Закройте главную форму, окно которой озаглавлено Forml. Выберите команду File/New, и будет выведено диалоговое окно New Items. Щелкните на вкладке Dialogs, и будет выведена страница диалогов. Выберите стандартный (Standard) диалог с кнопками, выровненными вертикально по правой стороне. Выберите опцию Inherit. Щелкните на кнопке ОК, и Delphi выведет новую диалоговую форму OKRightDlg2. Выберите из меню View/Forms, и будет выведено диалоговое окно View Form. Выберите шаблонную форму OKRightDIg, щелкните на кнопке ОК, и Delphi выведет новую шаблонную форму, озаглавленную Dialog и расположенную точно поверх новой формы. Теперь приступим к демонстрации. Переместите шаблонную форму в нижнюю часть главного окна Delphi. Вы обратили внимание, что новая форма OKRightDlg2 не открылась? Это связано с тем, что Delphi обновляет свойства Left и Тор одновременно с изменением соответствующих родительских свойств. Однако в обратном направлении изменения не передаются. Чтобы убедиться в этом, выполните следующие действия. Выберите команду View/Forms, и появится диалоговое окно View Form. Выберите OKRightDlg2 и щелкните на ОК. Переместите OKRightDlg2 в нижнюю часть экрана. Ваш экран должен выглядеть примерно так, как на рис. 1.11. Рис. 1.11. Изменение распространяются только в одном направлении — от родителей к потомкам При перемещении диалога-наследника на экране диалог-родитель будет оставаться на месте, что иллюстрирует передачу свойств только в одном направлении. Термин SDI (Single Document Interface) дословно означает одно-документный интерфейс и описывает приложения, способные загрузить и использовать одновременно только один документ. Программа Notepad, приведенная на рис. 1.12, является ярким представителем такого класса программ. Следует сказать несколько слов о термине документ. Приложения становятся все более объектоцентричными, т. е. они работают с неким центральным объектом, в который могут быть внедрены внешние объекты. В общем случае эти внешние объекты обрабатываются другим специализированным приложением. Примером может служить Wordpad (см. рис. 1.5), позволяющий внедрять любые OLE-объекты в свои файлы. Но он остается при этом SDI-приложением, так как может работать только с одним объектом (или документом в широком смысле этого слова) Wordpad. Рис. 1.12.Программа Notepad как пример SDI-приложения Способность одновременно работать только с одним объектом не мешает приложению использовать дополнительные формы, например диалоговые окна, панели инструментов и прочее (на рис. 1.13 показаны панели инструментов в окне Wordpad). Для реализации этих возможностей в Delphi просто добавьте форму в ваше приложение и установите ее свойство FormStyle равным fsSizeToolWin или fsToolWindow. Еще одним примером может служить сама Delphi — огромное количество панелей инструментов, меню, разнообразных библиотек компонентов, взаимодействующих между собой форм… Но в целом она остается SDI-приложением, так как может загрузить и использовать одновременно только один объект. Рис. 1.13. Программа Wordpad — SDI-приложение со многими формами Пример SDI-приложения Для демонстрации SDI создадим простую программу просмотра изображения. . Построение интерфейса Обычно первым шагом построения программы является создание интерфейса. Не будем отступать от традиций, и выполним следующие действия. 1. Выберите команду File/New Application, и появится пустое приложение. Совет: Delphi по умолчанию создает именно SDI-приложение. Однако хранилище объектов предоставляет возможность назначить новый шаблон проекта по умолчанию. 2. Установите следующие свойства форм. Свойство Значение Caption Image Viewer Name frmMain ShowHint True 3. Поместите компонент TPanel в форму. Установите следующие его свойства. Свойство Значение Align alTop Caption – 4. Поместите три компонента TSpeedButton в TPanel и назовите их spbtnLoad, spbtnStretch и spbtnCenter. Установите следующие их свойства. Свойство Значение spbtnLoad.Hint Load spbtnLoad.Left 8 spbtnLoad.Top 8 spbtnStretch.AllowAlIUp True spbtnStretch.Grouplndex 1 spbtnStretch.Hint Stretch spbtnStretch.Left 48 spbtnStretch.Top 8 spbtnCenter.AllowAlIUp True spbtnCenter.Grouplndex 2 spbtnCenter.Hint Center spbtnCenter.Left 80 spbtnCenter.Top 8 5. Поместите еще одну TPanel в форму и установите следующие ее свойства. Свойство Значение Align alClient Caption – 6. Поместите компонент ТImage во вновь созданную ТPanel и установите следующие его свойства. Свойство Значение Align alClient Name imgMain 7. Добавьте в форму TOpenDialog со следующими свойствами. Свойство Значение Filter Bitmaps (*.bmp)|*.bmp Name opndlgLoad Options [ofPathMustExist,ofFileMustExist] Delphi предоставляет вам множество значков для компонента TSpeedButton; они находятся в каталоге IMAGES\BUTTONS. Для нас вполне подойдут следующие установки свойств Glyph. Свойство Значение spbtnLoad.Glyph FLDROPEN.BMP spbtnStretch.Glyph FONTSIZE.BMP spbtnCenter.Glyph PICTURE.BMP Теперь самое время сохранить проект, выбрав в меню команду File/Save Project As. Сохраните Unit1 как Main, а проект — как EgSDIApp. Написание кода Теперь, после создания интерфейса, перейдем к написанию исходного текста вашего приложения. Сначала загрузите изображение следующим образом. 1. Дважды щелкните на компоненте spbtnLoad, и Delphi выведет окно редактора и автоматически создаст обработчик события OnClick. 2. Введите код. if opndlgLoad.Execute then imgMain.Picture.LoadFromFile(opndlgLoad.FileName); Метод opndlgLoad.Execute вызывает стандартное диалоговое окно открытия файла. Если вы выбрали файл и щелкнули на ОК, метод возвращает True и загружает в свойство FileName полный путь к имени файла. При щелчке на Cancel или нажатии клавиши Компонент TImage предоставляет свойство Picture, которое является экземпляром класса TPicture. Этот класс обеспечивает работу с растровыми изображениями, пиктограммами и метафайлами. Один из его методов, LoadFromFile, служит для загрузки изображения по имени файла. Выберите команду Run/Run для компиляции и запуска приложения и попытайтесь открыть картинку. Теперь добавьте возможность растягивания изображения. 1. Дважды щелкните на компоненте spbtnStretch, и Delphi выведет окно редактора и автоматически создаст обработчик события OnClick. 2. Введите код. imgMain.Stretch:= spbtnStretch.Down; Компонент TSpeedButton имеет свойство Down, которое равно True при нажатой кнопке. Свойство Stretch класса TImage позволяет растянуть картинку. Для выравнивания картинки по центру воспользуйтесь приведенной выше инструкцией (только используйте компонент spbtnCenter) и введите следующий код: imgMain.Center:= spbtnCenter.Down; Термин MDI (Multiple Document Interface) дословно означает многодокументный интерфейс и описывает приложения, способные загрузить и использовать одновременно несколько документов или объектов. Примером такого приложения может служить диспетчер файлов (File Manager). Обычно MDI-приложения состоят минимум из двух форм — родительской и дочерней. Свойство родительской формы FormStyle установлено равным fsMDIForm. Для дочерней формы установите стиль fsMDIChild. Родительская форма служит контейнером, содержащим дочерние формы, которые заключены в клиентскую область и могут перемещаться, изменять размеры, минимизироваться или максимизироваться. В вашем приложении могут быть дочерние формы разных типов, например одна — для обработки изображений, а другая — для работы с текстом. Создание форм В MDI-приложении, как правило, требуется выводить несколько экземпляров классов формы. Поскольку каждая форма представляет собой объект, она должна быть создана перед использованием и освобождена, когда в ней больше не нуждаются. Delphi может делать это автоматически, а может предоставить эту работу вам. Автоматическое создание форм По умолчанию при запуске приложения Delphi автоматически создает по одному экземпляру каждого класса форм в проекте и освобождает их при завершении программы. Автоматическое создание обрабатывается генерируемым Delphi кодом в трех местах. Первое — раздел интерфейса в файле модуля формы. type TForm1 = class (TForm) private {Закрытые объявления.} public {Открытые объявления.} end; В данном фрагменте кода объявляется класс TForm1. Вторым является место, в котором описывается переменная класса. var Form1: TForm1; Здесь описана переменная Form1, указывающая на экземпляр класса TForm1 и доступная из любого модуля. Обычно она используется во время работы программы для управления формой. Третье место находится в исходном тексте проекта, доступ к которому можно получить с помощью меню View/ Project Source. Этот код выглядит как: Application.CreateForm(TForm1, Form1); Процесс удаления форм обрабатывается с помощью концепции владельцев объектов: когда объект уничтожается, автоматически уничтожаются все объекты, которыми он владеет. Созданная описанным образом форма принадлежит объекту Application и уничтожается при закрытии приложения. Динамическое создание форм Хотя автоматическое создание форм полезно при разработке SDI-приложений, при создании MDI-приложении оно, как правило, неприемлемо. Для создания нового экземпляра формы используйте конструктор Create класса формы. Приведенный ниже код создает новый экземпляр TForm1 во время работы программы и устанавливает его свойство Caption равным 'New Form'. Form1:= TForm1.Create(Application); Form1.Caption:= 'New Form'; Конструктор Create получает от вас в качестве параметра потомка TComponent, который и будет владельцем вашей формы. Обычно в качестве владельца выступает Application, чтобы все формы были автоматически закрыты по окончании работы приложения. Вы можете также передать параметр Nil, создав форму без владельца (или владеющую собой — как вам больше нравится), но тогда закрывать и уничтожать ее придется вам. В случае возникновения необрабатываемой ошибки такая форма останется в памяти, что не говорит о высоком профессионализме программиста… В приведенном ниже коде Form1 указывает только на последнюю созданную форму. Если вам это не нравится, воспользуйтесь приведенным ниже кодом — возможно, он более точно отвечает вашим запросам: with TFormI.Create(Application) do Caption:= 'New Form'; Совет: При разработке MDI-приложения метод Show не нужен, так как Delphi автоматически показывает все вновь созданные дочерние MDI-формы. В случае SDI-приложения вы обязаны использовать метод Show. Даже при динамическом создании форм Delphi попытается навязать вам свои услуги по созданию экземпляра каждой формы. Чтобы отказаться от них, воспользуйтесь диалоговым окном Project Options, изображенным на рис. 1.14, и удалите классы форм из списка Auto-create forms. Рис. 1.14. Диалоговое окно Project Options позволяет установить опции для текущего проекта Если вы захотите получить доступ к отдельному дочернему экземпляру класса, используйте свойство MDIChildren, описываемое в следующем разделе. MDI-свойства TForm Объект TForm имеет несколько свойств, специфичных для MDI-приложений. ActiveMDIChild Это свойство возвращает дочерний объект TForm, имеющий в текущее время фокус ввода. Оно полезно, когда родительская форма содержит панель инструментов или меню, команды которых распространяются на открытую дочернюю форму. Например, представим, что проект использует дочернюю форму, содержащую элемент TMemo, названный memDailyNotes. Имя класса этой дочерней формы— TfrmMDIChild. Родительская форма содержит кнопку Clear в панели инструментов, которая удаляет содержимое memDailyNotes в активной дочерней форме. Вот как это реализуется. procedure TfrmMDIParent.spbtnClearClick(Sender: TObject); begin if not (ActiveMDIChild = Nil) then if ActiveMDIChild is TfrmMDIChild then TfrmMDIChild(ActiveMDIChild). memDailyNotes.Clear; end; В первой строке проверяется, равен ли ActiveMDIChild значению Nil, так как в этом случае обращение к объекту вызовет исключительную ситуацию. Совет: activemdichild равен Nil, если нет открытых дочерних форм или свойство FormStyle не равно fsMDIForm. Поскольку ActiveMDIChild возвращает объект TForm, компилятор не имеет доступа к memDailyNotes — объекту TfrmMDIChild. Вторая строка проверят соответствие типов, т. е. действительно ли ActiveMDIChild указывает на объект TfrmMDIChild. Третья строка выполняет преобразование типа и вызывает метод Clear компонента memDailyNotes. MDIChildren и MDIChildCount Свойство MDIChildren является массивом объектов TForm, предоставляющих доступ к созданным дочерним формам. MDIChildCount возвращает количество элементов в массиве MDIChildren. Обычно это свойство используется при выполнении какого-либо действия над всеми открытыми дочерними формами. Вот код сворачивания всех дочерних форм командой Minimize All. procedure TFormI.mnuMinimizeAllClick(Sender: TObject); var iCount: Integers; begin for iCount:= MDIChildCount-1 downto 0 do MDIChildren[iCount].WindowState:= wsMinimized; end; Если вы будете сворачивать окна в порядке возрастания элементов массива, цикл будет работать некорректно, так как после сворачивания каждого окна массив MDIChildren обновляется и пересортировывается, и вы можете пропустить некоторые элементы. TileMode Это — свойство перечислимого типа, определяющее, как родительская форма размещает дочерние при вызове метода Tile. Используются значения tbHorizontal (по умолчанию) и tbVertical для размещения форм по горизонтали и вертикали. WindowMenu Профессиональные MDI-приложения позволяют активизировать необходимое дочернее окно, выбрав его из списка в меню. Свойство WindowMenu определяет объект TMenuItem, который Delphi будет использовать для вывода списка доступных дочерних форм. Для вывода списка TMenuItem должно быть меню верхнего уровня. Это меню имеет свойство Caption, равное swindow. MDI-события TForm В MDI-приложении событие OnActivate запускается только при переключении между дочерними формами. Если фокус ввода передается из не MDI-формы в MDI-форму, генерируется событие OnActivate родительской формы, хотя ее свойство Active никогда и не устанавливается равным True. Эта странность на самом деле строго логична: ведь, если бы OnActivate генерировался только для дочерних форм, не было бы никакой возможности узнать о переходе фокуса ввода от другого приложения. MDI-методы TForm Специфичные для MDI-форм методы перечислены ниже. Arrangelcons выстраивает пиктограммы минимизированных дочерних форм в нижней части родительской формы. Cascade располагает дочерние формы каскадом, так что видны все их заголовки. Next и Previous переходит от одной дочерней формы к другой, как будто вы нажали Tile выстраивает дочерние формы так, что они не перекрываются. Пример MDI-приложения В этом разделе мы расширим возможности созданной ранее программы просмотра изображений. Создание интерфейса Интерфейс MDI-приложения очень похож на интерфейс разработанного ранее SDI-приложения, но каждое изображение выводится в отдельной, а не в главной форме. Выполните следующие действия для создания родительской формы. 1. Выберите команду File/New Application, и появится пустое приложение. 2. Установите следующие свойства. Свойство Значение Caption Image Viewer FormStyle fsMDIForm Name frmMDIParent ShowHint True 3. Поместите компонент TPanel в форму. Установите следующие его свойства. Свойство Значение Align alTop Caption – 4. Поместите три компонента TSpeedButton в TPanel и назовите их spbtnLoad, spbtnStretch и spbtnCenter. Установите следующие их свойства. Свойство Значение spbtnLoad.Hint Load spbtnLoad.Left 8 spbtnLoad.Top 8 spbtnStretch.AllowAlIUp True spbtnStretch.Grouplndex 1 spbtnStretch.Hint Stretch spbtnStretch.Left 48 spbtnStretch.Top 8 spbtnCenter.AllowAlIUp True spbtnCenter.Grouplndex 2 spbtnCenter.Hint Center spbtnCenter.Left 80 spbtnCenter.Top 8 Свойства Glyph установите те же, что и для SDI-приложения. 5. Добавьте в форму компонент TOpenDialog и установите следующие его свойства. Свойство Значение Filter Bitmaps (*.bmp)]*.bmp Name opndlgLoad Options [ofPathMustExist,ofFileMustExist] Теперь создадим дочернюю форму. 1. Выберите из меню File/New Form, и появится пустая форма. 2. Установите следующие ее свойства. Свойство Значение FormStyle fsMDIChild Name frmMDIChild Position poDefaultPosOnly 3. Поместите компонент TImage во вновь созданную форму и установите его следующие свойства. Свойство Значение Align alClient Name imgMain Удалите дочернюю форму из списка автоматически создаваемых форм следующим образом. 1. Выберите команду Project/ Options, и появится диалоговое окно Project Options, показанное на рис. 1.14. 2. Выберите frmMDIChild в списке Auto-create forms. 3. Щелкните на кнопке. Форма frmMDIChild при этом будет перенесена в список Available forms. 4. Щелкните на кнопке ОК. Теперь самое время сохранить проект, выбрав команду File/Save Project As. Сохраните Unit1 как MDIParent, а проект — как EgMDIApp. Написание кода Создав интерфейс, перейдем к написанию исходного текста приложения, который будет очень похож на код для SDI-приложения. Сначала загрузим изображение. Введите следующий код в обработчик события OnClick компонента spbtnLoad. procedure TfrmMDIParent.spbtnLoadClick(Sender: TObject); begin if opndlgLoad.Execute then with TfrmMDIChild.Create(Application) do begin Caption:= opndlgLoad.FileName; imgMain.Picture.LoadFromFile(opndlgLoad.FileName); ClientWidth:= imgMain.Picture.Width; ClientHeight:= imgMain.Picture.Height; end; end; После запуска диалогового окна создается новый экземпляр дочерней формы и загружается файл изображения. После загрузки размеры дочерней формы изменяются так, чтобы можно было видеть все изображение. Еще пара штрихов — и приложение заработает, как и предусматривалось. Поскольку модуль ссылается на тип TfrmMDIChild, находящийся в модуле MDIChild, после строки implementation следует добавить еще одну строку: uses MDIChild; Теперь можно приступить к компиляции и запуску приложения. Однако заметьте, что, когда вы щелкаете на кнопке Close, дочерняя форма не закрывается, а сворачивается в пиктограмму. Чтобы заставить ее закрыться, следует добавить в код обработчика OnClose класса TfrmMDIChild маленькую деталь — изменить свойство Action: Action:= caFree; Компоненты TSpeedButton Stretch и Center выполняют те же функции, что и в SDI-приложении, однако их обработчики события OnClick следует изменить следующим образом if not (ActiveMDIChild = Nil) then if ActiveMDIChild 15 TfrmMDIChild then TfrmMDIChild(ActiveMDIChild). imgMain.Stretch:= spbthStretch.Down; и if not (ActiveMDIChild = Nil) then if ActiveMDIChild is TfrmMDIChild then TfrmMDIChild(ActiveMDIChild). imgMain.Center:= spbthCenter.Down; Остается последняя проблема — состояния кнопок Stretch и Center одинаковы для всех дочерних форм Для решения этой задачи добавьте в обработчик события OnActivate класса TfrmMDIChild строки. frmMDIParent.spbtnStretch.Down:= imgMain.Stretch; frmMDIParent.spbtnCenter.Down:= imgMain.Center; И, наконец, самый последний из последних штрихов — в модуле MDIChild добавьте после строки implementation строку. uses MDIParent; Компилируйте, запускайте и смотрите. MDI-приложение создано! ПРЕДОСТЕРЕЖЕНИЕ: В этом примере присвоение нового значения свойству down класса TSpeedButton вызывало событие Оn-click. Будьте осторожны при написании кода обработчика события, который генерирует новое событие путем присвоения значения свойству, ведь при этом можно создать бесконечную рекурсию. А теперь забудьте обо всех этих кнопочках, пиктограммах и пропахших мышами приложениях — настоящие программисты и пользователи обожают текстовый режим! Если же говорить серьезно, иногда программы в текстовом режиме могут быть полезными. Примерам таких программ несть числа, и основная проблема всегда заключалась в том, что они запускались в окне DOS и не имели доступа к Windows API или к таким ресурсам, как принтеры и память (не говоря уже о том, что это были 16-разрядные приложения с 640-килобайтовым ограничением на используемую память). Теперь, когда Delphi поддерживает создание консольных приложений, можно создавать 32-разрядные текстовые приложения с использованием линейной модели памяти и с возможностью работать с Windows API и применять ресурсы Обычно консольные приложения используются, когда не нужно вводить информацию большого объема и требования к интерфейсу сведены к минимуму. Ну и заметим к тому же, что простенькое „Hello, world!“, созданное путем размещения компонента TLabel в форме, будет иметь размер около 150 Кбайт, в то время как консольное „Hello, world!“ уложится в 10… Пример консольного приложения Лучший способ изучить консольные приложения — создать пресловутое „Hello, world!“. Для создания его вручную сделайте следующее. 1. Выберите команду File/New Application. 2. Выберите команду File/Remove From Project, и появится диалоговое окно, Remove From Project, показанное на рис. 1.15. 3. В проекте содержится один модуль формы. Выберете его и щелкните на кнопке ОК. Появится диалоговое окно Save changes to Unit1.pas? 4. Щелкните на кнопке No, и форма будет удалена из проекта. Сохраните проект как EgConsoleHello. Рис. 1.15.Диалоговое окно Remove From Project позволяет удалять модули и формы из проекта. Хотя мы создаем „бесформенное“ приложение, оно все еще не является консольным и использует GUI, а значит, сравнимо по размеру с бронтозавром. Выберите команду View/Project Source, и в редакторе появится следующий текст. program EgConsoleHello; uses Forms; {$R *.RES} begin Application.Initialize; Application.Run; end; Этого слишком много, чтобы быть достаточным. Вы видите, что подключен модуль Forms, однако он не используется, поэтому данную строку можно удалить. Строки с Application используются для инициализации OLE-сервера и вывода главной формы. Поскольку мы не используем ни того, ни другого, удалите и эти строки. Последнее действие — объяснить компилятору, что мы хотим создать обычное, простое, незамысловатое консольное приложение. Этого можно достичь с помощью команды $APPTYPE. Код в результате будет выглядеть так. program EgConsoleHello; {$APPTYPE CONSOLE} {$R *.RES} begin end; Ax да! Мы же собирались вывести свое приветствие! Для этого добавьте между begin и end строку WriteLn ('Hello, world!'); Сохраните, скомпилируйте и запустите проект из командной строки. Надеюсь, вам понравилось? И еще одно замечание — консольные приложения используют стандартные потоки ввода-вывода, а значит, вы можете использовать функции Read, ReadLn, Write и WriteLn. Функции Windows API для консольного приложения Вы можете делать гораздо больше в консольном приложении, чем просто считывать и выводить текст. Вам доступно около 40 функций Windows API для работы с дисплеем, например изменение атрибутов выводимых символов или изменение размеров консоли. В листинге 1.1 изменяется текст, выводимый в заголовке консольного окна. Вы можете найти этот пример на дополнительно распространяемой дискете в папке EgConsoleTitle. Листинг 1.1. Изменение заголовка консольного окна program EgConsoleTitle; {$APPTYPE CONSOLE} uses Windows, SysUtils; {$R *.RES} var sNewTitle, sErrMsg: String; begin sNewTitle:= 'Welcome to Con5ole World'; if not SetConsoleTitle(P Char(sNewTitle)) then begin sErrMsg:= 'Unable to set caption — '+SysErrorMessage(GetLastError); MessageBox(0,P Char(sErrMsg),'Error',MBICONEXCLAMATION+MBOK); end; ReadLn; end. Здесь функция API SetConsoleTitle возвращает False, если назначить новый заголовок невозможно. GetLastError возвращает числовое значение последней ошибки API, которое SysErrorMessage конвертирует в строку для вывода на экран. Совет: Поставляемая с Delphi система справки по Win32 содержит функции консольного API. Для доступа к ним выведите пункт Console Reference и щелкните на кнопке. Возможности Object Repository не ограничиваются хранением форм, модулей и диалоговых окон (даже целые проекты могут быть сохранены в виде шаблонов). Шаблон проекта может содержать формы, модули, и пользовательский код и использоваться в качестве отправной точки для создания нового проекта. Использование шаблонов проектов Delphi поставляется с тремя шаблонами проектов. MDI Application создает полностью функциональный MDI-проект. Родительская форма включает меню, кнопки Speed Button и строку состояния. Проект также содержит замещаемый код, реализующий функции меню и управляющий сообщениями в строке состояния. SDI Application содержит простой SDI-проект. Основная форма содержит меню, кнопки Speed Button и строку состояния. В проект также включено диалоговое окно About и реализующий его код. Win95 Logo Application создает проект, моделирующий основные принципы, которые установлены Microsoft для нее Win95 Logo certification. Если вы будете придерживаться этого стиля и получите сертификат Microsoft, то сможете вывести логотип „Designed for Win95“. Для создания нового SDI-поиложения с использованием шаблона выполните следующие действия. Выберите команду File/New, и появится диалоговое окно New Items (см. рис. 1.6). Щелкните на вкладке Projects, что приведет к появлению соответствующей страницы. Выберите пиктограмму SDI Application, щелкните на кнопке ОК, и появится диалоговое окно Select Directory, показанное на рис. 1.16. Определите каталог для нового проекта. Если вы выберете несуществующий каталог, Delphi создаст его для вас. Щелкните на кнопке ОК, и новый проект будет создан. При создании проекта доступна только опция Copy. Все файлы проекта копируются в ваш каталог и изменения в них не приведут к изменению шаблона проекта в хранилище. На странице Projects содержится еще одна пиктограмма— Application Expert. Эксперт приложений, показанный на рис. 1.17, проведет вас через все этапы создания нового проекта. Рис. 1.16.Диалоговое окно Select Directory позволяет выбрать каталог для нового проекта. Рис. 1.17. application Expert поможет вам создать новый проект. Добавление шаблонов проектов Помимо стандартных, вы, вероятно, захотите иметь и собственные шаблоны. Для этого добавьте их в Object Repository следующим образом. Создайте проект, добавьте в него код и объекты по вашему усмотрению. Сохраните проект. Вернитесь к разделу „Разделяемое хранилище“, чтобы вспомнить о том, где и как хранить шаблоны. Выберите команду Project/Add To Repository, после чего будет выведено диалоговое окно Add to Repository, показанное на рис. 1.18. Введите запрашиваемую информацию, щелкните на кнопке ОК, и проект будет добавлен в Object Repository в качестве шаблона. Рис. 1.18.Диалоговое окно Add to Repository позволяет добавить собственный шаблон проекта в Object Repository Существует огромное количество опции, которые можно изменять при разработке проекта. Они представлены на вкладках диалогового окна Project Options (см. рис. 1.14). Вы всегда можете установить опции по умолчанию, отключив переключатель Default в нижнем левом углу диалогового окна Project Options. заметку Forms Раскрывающийся список Main form определяет главную форму вашего приложения, которую Delphi выводит первой и закрывает, когда приложение завершает работу. Два списка. Auto-create forms и Available forms, позволяют определить автоматически создаваемые формы. Application Поле Title определяет название вашего приложения, выводимого в панели задач (например, Delphi устанавливает его по имени текущего проекта). Если вы не введете названия, будет использоваться название, установленное по умолчанию (DELPHI32). Поле Help file определяет файл справки, подключаемый к вашему приложению, что позволяет использовать систему контекстной справки. Кнопка Load Icon позволяет установить пиктограмму приложения, используемую как пиктограмму по умолчанию в ярлыках и панели задач. С помощью последней опции. Target file extension, можно переопределить стандартное расширение создаваемого файла (DLL — для динамически линкуемых библиотек, ОСХ — для элементов ActiveX и т. п.). Однако это расширение можно не устанавливать, так как Delphi весьма корректно работает по умолчанию. Compiler Вкладка компилятора содержит огромное количество переключателей, позволяющих устанавливать опции компилятора. Две особенно полезные опции. Show hints и Show warnings, помогут вам при отладке (при этом компилятор будет выдавать множество предупреждений, например об использовании не инициализированной переменной). Linker Опция Map file полезна для тех программистов, которые интересуются технической информацией, например адресами сегментов, стартовым адресом программы и т. п. Linker output определяет, что именно будет выдавать компилятор — Delphi Compiled Unit (DCU) или объектные файлы (OBJ). Последние могут применяться, например, при разработке программ с использованием двух языков. Опции ЕХЕ и DLL позволяют создавать консольные приложения, описанные в разделе „Создание консольного приложения“, и включать отладочную информацию, о которой подробно рассказывается в главе 2, „Тестирование и отладка“. Memory sizes определяет минимальный и максимальный размеры стека приложения и устанавливает предпочтительный базовый адрес для вашей DLL. Если ваша программа часто использует глубокую рекурсию или большие локальные переменные, увеличьте размер стека. Базовый адрес лучше не изменять, если вы не совсем уверены в том, что собираетесь делать. ЕХЕ Description представляет возможность внести в ЕХЕ или DLL строку описания приложения (что-то вроде „MixFix (С) 1993–1997 KIV without Co“). Directories/Conditionals Установки Output и Unit output определяют, где компилятор размещает ЕХЕ или DLL, а также скомпилированные модули. Если оставить опции незаполненными, создаваемые модули будут располагаться там же, где и исходные тексты, а выходные выполняемые файлы или DLL — в папке проекта. Conditional defines определяют флаги, проверяемые в процессе компиляции и используемые, как правило, для включения или исключения блоков кода из проекта при компиляции. Unit Aliases существует для совместимости со старыми версиями Delphi (в свое время модуль Windows был разбит на два файла и для сборки одного модуля следовало присвоить один и тот же псевдоним Windows обоим файлам). VersionInfo Вкладка Versionlnfo дает возможность добавить к выполняемому модулю или DLL информацию о версии— Major Version, Minor Version и File Description. Действительно полезную возможность предоставляет Auto-increment build number, заставляя Delphi всякий раз увеличивать номер выпуска при компиляции программы. Раздел Module Attributes позволяет включать флаги, такие как Debug Build, в приложение. Выбор опций не влияет на процесс компиляции — они используются только в информативных целях. Packages Вкладка Packages позволяет определить, какие пакеты доступны для использования при разработке приложения, и прилинковать их при создании результирующего файла. Группа Design packages предоставляет список зарегистрированных пакетов, которые можно выбрать для использования в приложении. Группа Runtime packages дает возможность определить, какие пакеты компоновщик будет использовать при построении выходного файла. По умолчанию опция Build with runtime packages отключена, а это означает, что все объекты из VCL будут скомпонованы с вашим приложением. Включение опции означает, что ваше приложение будет разделять с другими приложениями Delphi одну копию пакетов. Сейчас, когда вы ближе познакомились с тремя типами приложений и разобрались в концепции повторного использования форм и приложений, пришла пора поговорить „о сургуче и башмаках“, а точнее, о двух очень полезных компонентах— TApplication и TScreen. Они снабжают вас информацией об операционной среде выполнения программы и предоставляют возможность повысить профессионализм вашего приложения Компонент TApplication Компонент TApplication инкапсулирует приложения при выполнении. Delphi автоматически создает экземпляр Application класса TApplication при выполнении приложения. Для использования этого объекта включите модуль Forms в раздел uses. Свойства TApplication TApplication предоставляет несколько свойств, с помощью которых можно отслеживать состояние приложения и контролировать некоторые аспекты его поведения. Active Это свойство возвращает значение True, если в приложении в текущий момент содержится фокус ввода. Если Active приложения имеет значение True, то и свойство Active для одной из форм также равно True. Обычно оно используется для определения, имеет ли приложение фокус ввода, перед выводом строки состояния или прорисовкой объекта TCanvas. Совет: Даже будучи неактивным, приложение продолжает получать сообщения о перемещении мыши. EXEName Это свойство представляет собой строку, содержащую полный путь выполняемого файла (аналог в С— argv[0]). Чаще всего оно используется для определения рабочего каталога приложения, в котором могут находиться файлы данных или инициализации программы. Совет: Для получения из EXEName рабочего каталога используйте функцию ExtractFilePath или Extract-FileDir из модуля SysUtils. Hint и ShowHint В свойстве hint содержится текст, который будет выведен после запуска события OnHint. Свойство ShowHint используется для определения, выводится ли подсказка „на лету“. Установка свойства равным False отключает систему подсказок для всех элементов в приложении независимо от их индивидуальных установок. Обычно оно используется совместно с пунктом меню, включающим или отключающим подсказки. Вот примерный код. procedure TFormI.mnuToggleHintsClick(Sender: TObject); begin {Переключение текущего состояния.} mnuToggleHints.Checked:= not mnuToggleHintsChecked; {Обновление свойства ShowHint.} Application.ShowHint:= mnuToggleHints.Checked; end; HintColor, HintPause, HintHidePause и HintShortPause hintcolor определяет цвет окна всплывающей подсказки, например Application.HintColor:= cILime; HintPause и HintHidePause определяют временные задержки при показе подсказок следующим образом. Указатель мыши помещается над потомком TControl. Событие OnHint вызывается сразу же после установки указателя мыши на TControl. Delphi ожидает HintPause миллисекунд перед выводом окна подсказки. Указатель остается над TControl. Delphi ожидает HintHidePause миллисекунд перед закрытием окна подсказки. Свойство HintShowPause определяет задержку перед отображением подсказки, если активна подсказка другого элемента. Icon Свойство Icon позволяет изменить пиктограмму, представляющую приложение в панели задач во время работы. Это полезно, если необходимо отмечать, таким образом, изменение состояния приложения. Следующий код назначает пиктограмму из файла INACTIVE.ICO: Application.Icon.LoadFromFile('INACTIVE.ICO'); Title Определяет заголовок приложения в панели задач. Методы TApplication Minimize и Maximize Эти методы заставляют приложение принять свернутый и развернутый вид. Вы можете удивиться, зачем это необходимо при наличии свойства WindowState объекта TForm. Однако так вы можете минимизировать форму на рабочем столе, но не в панели задач. Описываемый здесь метод осуществляет эту операцию более корректно. ProcessMessages Этот метод вынуждает приложение обработать ожидающие сообщения. Это полезно делать, например, в длинном цикле, чтобы приложение могло реагировать на поступающие сообщения. Предположим, ваше приложение состоит из цикла while not Application.Terminated do При этом оно не сможет обрабатывать сообщения, а вы не сможете перемещать окна и воздействовать на его элементы управления. Одним словом, несмотря на деятельность в цикле, приложение оказывается мертвым для внешнего мира. Однако все изменяется при таком варианте кода: while not Application.Terminated do Application.ProcessMessages; Совет: Программисты часто предпочитают использовать для длительных вычислений отдельную подзадачу, реализуемую классом TThread. Terminate Этот метод — предпочтительный способ закрытия приложения. Terminate не закрывает приложение немедленно, а дожидается обработчика события и завершения всех других процессов. Обычно оно используется в меню File/Exit. procedure TFormI.mnuFileExitClick(Sender: TObject); begin Application.Terminate; end; События TApplication Обработка событий TApplication Поскольку при создании TApplication недоступен, установка его обработчика события затруднена из-за невозможности использования инспектора объектов (Object Inspector). Сначала опишем обработчик события. Поскольку событие распознается и запускается объектом, обработчик должен быть методом объекта. Он также должен соответствовать соглашению о вызовах события, которое обычно описано в системе справки. Например, из файлов справки можно узнать, что событие OnActivate типа TNotifyEvent является наиболее общим типом. Его описание его выглядит так: TNotifyEvent = procedure (Sender: TObject) of Object; Это описание говорит о том, что ваш обработчик должен получать параметр TObject, который позволяет определить, какой именно объект распознал и отослал событие. Создадим новое приложение и модифицируем описание TForm1. type TForm1 = class(TForm) private {Закрытые объявления.} procedure OnActivateHandler(Sender: TObject); public {Открытые объявления.} end; Описание обработчика как protected не несет особой нагрузки и принято по умолчанию. Важно лишь, чтобы он был методом класса. Теперь, когда мы описали обработчик, определим его. Добавьте следующий код в раздел implementation вашего модуля. procedure TFormI.OnActivateHandler(Sender: TObject); begin {Код вашего обработчика.} end; Совет: Определение процедуры или функции не требует перечисления списка параметров, так как он был дан при описании. Вы можете его повторить для повышения удобочитаемости и ясности программы. Наконец, назначим обработчик событию. Обычно это выполняется в событии OnCreate главной формы. Измените обработчик события OnCreate класса Tform1 следующим образом. procedure TFormI.FormCreate(Sender: TObject); begin Application.OnActivate:= OnActivateHandler; end; OnActivate и OnDeactivate Эти события оповещают программу об изменении свойства Active. OnException Событие вызывается при необработанной исключительной ситуации. OnHint Событие генерируется при перемещении указателя мыши над объектом — потомком TControl, если его свойство Hint равно значению, которое отличается от пустой строки. Onldle Событие генерируется, когда приложение ожидает ввода и не занято обработкой события. Обычно оно используется для выполнения фоновых задач наподобие загрузки базы данных или обработки изображения. Обработчик получает логический параметр Done, по умолчанию равный True. Если вы оставляете его равным True, обработчик не запустится до тех пор, пока не будет получено и обработано очередное сообщение. Если вы установили Done равным False, обработчик будет запускаться во время ожидания сообщения. Поскольку во время работы обработчика приложение не отрабатывает сообщения, делайте его более коротким либо не забывайте запускать из него процедуру ProcessMessages. Ниже приведен код использования обработчика для вывода текущего времени в объекте TPanel. procedure TFormI.OnIdleHandler(Sender: TObject; var Done: Boolean); begin pnlTime.Caption:= TimeToStr(Now); end; Компонент TScreen Класс TScreen инкапсулирует состояние экрана или выводимой области. Delphi во время работы автоматически создаст экземпляр класса Screen. Для его использования в раздел uses нужно включить модуль Forms. Свойства TScreen ActiveControl Это свойство возвращает объект TWinControl, имеющий фокус ввода. Обычно оно используется для реализации команд Copy, Cut и Paste для текстовых управляющих элементов. Следующий код, помещенный в обработчик TMenuItem, выполняет функцию Copy. procedure TFormI.mnuEditCopyClick(Sender: TObject); begin Screen.ActiveControl.Perform(WMCOPY,0,0); end; Все, что происходит в мире Windows, базируется на сообщениях. Для выполнения какой-либо функции управляющие элементы часто отсылают сообщения самим себе. Так и в этом примере управление происходит путем передачи сообщения WMCOPY. ActiveForm Это свойство возвращает объект TForm, имеющий фокус ввода. Если приложение неактивно, свойство указывает, какая именно форма будет иметь фокус ввода при активизации приложения. В качестве примера используем свойство для создания мигающего заголовка формы, чтобы привлечь внимание пользователя. Функция Windows API, предназначенная для этой цели, должна получить дескриптор окна: FlashWindow(Screen.Active Form.Handle,False); Cursor Это свойство определяет форму указателя мыши для всего приложения. Обычно оно используется для отображения песочных часов, чтобы в то время, пока пользователь думает о сложной работе, выполняемой приложением, немного передохнуть. {Придать курсору форму песочных часов.} Screen.Cursor:= crHourglass; try {Попытаемся ничего не делать какое-то время.} for iCount:=1 to 1000000000 do; finally {Восстановим форму указателя.} Screen.Cursor:= crDefault; end; Форму указателя можно изменить для каждого потомка TControl (включая ТForm) отдельно. Forms и FormCount Эти свойства возвращают список форм и их количество. Работа с ними ничем не отличается от работы со списком дочерних окон, описанных в разделе „MDIChildren и MDIChildCount“. Height и Width Это наиболее полезные свойства, возвращающие высоту и ширину экрана в пикселях. Они могут применяться во многих ситуациях, например при определении местоположения формы. Вот как поместить форму в центре экрана. Left:= (Screen.Width — Width) div 2; Top:= (Screen.Height — Height) div 2; События TScreen tscreen предоставляет два основных события, извещающие программу об изменении фокуса ввода. Подобно возможностям класса TApplication, возможности класса TScreen доступны лишь при выполнении. OnActiveControlChange Это событие возникает при передаче фокуса ввода от одного управляющего элемента к другому. Свойство ActiveControl обновляется непосредственно перед вызовом события. Вы можете использовать событие, например, для вывода текста подсказки в строке состояния. procedure TFormI.ActiveControlChangeHandler(Sender: TObject); begin if (not Application.Terminated) then pnlStatus.Caption:= ActiveControl.Hint; end; Проверка связана с тем, что событие генерируется и при закрытии приложения, а при этом обращение к уже уничтоженному объекту вызовет исключительную ситуацию. OnActiveFormChange Событие генерируется при создании новой формы или передаче фокуса ввода от одной формы к другой. Обычно это событие используется в MDI-приложениях для обновления доступных функций меню и кнопок-ускорителей. Ниже приведен пример из шаблона MDI-приложения. procedure TMainForm.UpdateMenuItems(Sender: TObject); begin FileCloseItem.Enabled:= MDIChildCount 0; FileSaveItem.Enabled:= MDIChildCount 0; FileSaveAsItem.Enabled:= MDIChildCount 0; end; Процедура UpdateMenuItems назначена в качестве обработчика событию OnActiveFormChange в обработчике OnCreate. procedure TMainForm.FormCreate(Sender: TObject); begin Application.OnHint:= ShowHint; Screen.OnActiveFormChange:= UpdateMenuItems; end; Разделяемые обработчики событий Как вы уже знаете, каждый класс способен генерировать свои собственные события. Каждое из них имеет определенный тип, как, например, TNotifyEvent у OnClick и TCloseEvent у OnClose. Delphi позволяет написать один обработчик события и назначить его нескольким событиям одновременно. Представьте себе объект TEdit, генерирующий события OnKeyDown и OnKeyUp. Поскольку оба события — одного типа, можете написать одну процедуру и назначить ее обоим событиям. Процедура будет вызываться дважды при каждом нажатии клавиши (при нажатии и отпускании). Или, например, вы можете создать один обработчик для событий OnCreate и OnClick. Еще одно общее назначение разделяемых обработчиков — обработка событий от двух различных управляющих элементов, которые могут и не быть экземплярами одного класса. Вот как создать разделяемый между классами TButton и TEdit обработчик OnClick. Выберите из меню File/New Application для создания приложения. Поместите TButton в форму и введите в обработчик OnClick следующий код. procedure TFormI.ButtonlClick (Sender: TObject); begin Editl.SetFocus; Editl.SelectAll; end; Поместите TEdit в форму. В Object Inspector выберите в списке для события OnClick обработчик ButtonClick. Теперь после щелчка на кнопке и на объекте TEdit будут выполняться одни и те же действия, фокус ввода будет передаваться управляющему элементу TEdit, и весь текст в нем будет выделяться. Параметр Sender Поскольку множество объектов может разделять один обработчик событий, необходимо уметь выяснять, какой именно обработчик его сгенерировал. Для этого предназначен параметр Sender, передаваемый в каждый обработчик, который указывает на объект, сгенерировавший событие. Часто этот параметр используется для различных действий обработчика в зависимости от того, кто именно породил событие. Создание одноэкземплярного приложения Если в Windows несколько раз щелкнуть на пиктограмме приложения, по умолчанию запустится сразу несколько его копий. Чтобы избежать этого, т. е. не дать запуститься второй копии приложения, необходимо выполнить поиск по заголовкам окон. Поиск по заголовкам окон При создании экземпляра окна Windows требует зарегистрировать имя класса окна (window class name). Delphi использует класс формы в качестве имени класса окна, например, когда Delphi создает экземпляр Form1 класса TForm1, TForm1 используется в качестве имени для регистрации окна Form1. Каждая форма имеет свойство Caption, известное как заголовок окна (window title). Эти два параметра позволяют искать окно с помощью функции Windows API FindWindow, возвращающей дескриптор найденного окна. Эту функцию нельзя вызывать из класса TForm1, так как к тому времени окно уже будет создано, и вы окажетесь в глупом положении, найдя сами себя. Вот код, который должен находиться в файле проекта. begin Application.Initialize; if FindWindow('TFormi','Formi') о 0 then Application.Terminate; Application.Create Form(TForm1,Form1); Application.Run; end. Поскольку вы используете функцию Windows API, проследите, чтобы был подключен модуль Windows. Если вы запускаете это приложение из Delphi, учтите, что Form Designer уже создал такое окно, и вы всегда сможете его найти. Это приложение следует запускать отдельно, закрыв Delphi. Изменив свойство Caption или Name, вы рискуете не найти своего окна и придется повозиться с кодом программы, чтобы отследить эти изменения. Может возникнуть ситуация, когда простое совпадение приведет к тому, что окно будет найдено в совсем другом приложении, которое будет опознано как свое. Активизирование существующей копии Все-таки, сказать пользователю „Ты уже запустил одну копию, теперь иди и ищи ее!“ — как-то негуманно… Более профессиональным решением будет активизировать существующую копию с помощью другой функции Windows API — SetForegroundWindow. Измените проект следующим образом. var hwndPrev: HWND; begin Application.Initialize; hwndPrev:= FindWindow('TFormi','Formi'); if hwndPrev < 0 then begin SetForegroundWindow(hwndPrev); Application.Terminate; end; Application.CreateForm(TFormi,Formi); Application.Run; end.