    Создание приложений 

    ГЛАВА 8 

    Ааще всего Delphi используется для создания приложений. Вы можете 
создавать приложения любого типа — от утилит командной строки до программы 
электронной почты или многопользовательской финансовой базы данных. 
    В главах 6. "Использование компонентов Delphi", и 7, "Общие элементы 
управления Win32" рассказывалось о компонентах— основных строительных блоках 
приложения. В этой главе вы расширите свой кругозор и познакомитесь с 
созданием приложений более детально, так же, как и с тремя основными типами 
приложений. В качестве премии будет приведено несколько с оветов о том, как 
ускорить создание программ и сделать их более профессиональными. 

    Работа с формами 

    Подобно фундаменту здания, форма представляет собой фундамент программы, 
на котором строится все остальное. Форма — это место, где пользователь 
общается с программой. Приложение может иметь несколько форм, каждая из 
которых выполняет свое особое предназначение. 
    Delphi инкапсулирует концепцию форм в класс TForm, и каждая форма, 
создаваемая с помощью конструктора форм (Form Designer), наследует основные 
свойства, методы и события этого класса. 

    На заметку 
    Класс TForm не является прямым потомком TWinControl. Свойства, методы и 
события этого класса описаны в главе 6, "Использование компонентов Delphi". 

    Свойства TForm 

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

    Active 

    Свойство Active определяет, имеет ли форма фокус ввода. Если имеет, оно 
возвращает True, если нет — False. Windows выводит заголовок активной формы 
цветом, отличающимся от цвета неактивных. 

    Работа с формами 

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

    О типах приложений 

    Здесь описывается три типа приложений — однодокументное (SDI), 
многодокументное (MDI) и консольное. 

    Ключевые компоненты приложений 

    Ознакомьтесь с ключевыми компонентами выполнения приложения (TApplication 
И TScreen). 

    Пара трюков 

    Хитрые трюки помогут вам сделать программу (а значит, и жизнь) не столь сложной. 

212


    На заметку 
    11еактивные окна продолжают получать сообщения о перемещении и выборе мыши. 

    Независимо от типа приложения в один момент времени может быть активной 
только одна форма. Вы можете обратить внимание на то, что заголовок 
родительской формы в MDI-приложении изображен "активным" цветом. Не попадитесь 
на эту удочку — свойство Active родительского MDI-окна никогда не бывает 
равным True. 
    Свойство ActiveForm класса TScreen, описанное ниже в этой главе, 
возвращает форму, которая в данный момент имеет фокус ввсда 

    [ Совет I 

    ActiveControl 

    Свойство ActiveControl указывает на потомка TWinControl, имеющего в 
настоящий момент фокус ввода. Вы можете установить значение во время создания 
программы, определив, какой элемент будет иметь фокус ввода при инициализации 
формы. Назначение ActiveControl во время работы программы— установка фокуса 
ввода в поле с некорректно введенными данными. Пр иведенный ниже фрагмент кода 
позволяет проверить текст, введенный в элемент edtCustNameTnepei закрытием формы. 

    procedure  TDataEntryForm.FormCloseQuery(Sender:   Tobject; 
    var  CanClose:   Boolean); begin 
    {Проверяем,   введен ли текст в  элемент.} 
    if edtCustName.Text  =   ''   then 
    begin 
    {Запрещаем закрытие.} CanClose   :=  False; 
    {Устанавливаем фокус  в  поле  с  некорректными данными.} ActiveControl   
:=  edtCustName; ; 
    end; end; 

    Метод SetFocus потомков TWinControl устанавливает фокус ввода и обновляет 
свойство  ActiveControl. 

    Совет * 
    Большинство событий передает параметр Sender своему обработчику. Sender 
определяет, какой элемент обнаружил событие и запустил обработчика. Этот 
параметр детально описан далее в этой главе. 

    Г Совет 1 
    На      i заметку 

    AutoScroll, HorzScrollBar и VertScrollBar 

    Свойство AutoScroll управляет появлением полос прокрутки в форме, размеры 
которой не позволяют вывести все ее элементы одновременно. Если свойство равно 
True и вы изменили размеры так, что не все элементы формы видны, в форме 
автоматически появляются полосы прокрутки. Если же значение свойства— False, 
вы теряете доступ к элементам формы, не помест ившимся на экране. 

    Г Совет 1 
    Компонент TScrollBar, описанный в главе 6, "Использование компонентов 
Delphi", позволяет прокручивать форму независимо от свойства AutoScroll. 

    Глава 8. Создание приложений      213 

    Вы можете управлять полосами прокрутки с помощью свойств HorzScrollBar и 
VertScrollBar. Это весьма полезно в программах, реализующих свойство 
распахивания окна. Поскольку размеры выводимой диаграммы могут быть больше 
размеров формы, а выводите вы ее самостоятельно, AutoScroll не активизируется, 
и вам следует обеспечить прокрутку и рисование, самом 
    у. Вот пример такого кода, обеспечивающего прокрутку в двойном размере формы. 

    {Устанавливаем диапазон вертикальной прокрутки.} VetrScrollBar.Range   :=  
Height   *   2; {Показываем вертикальную полосу прокрутки.} VertScrollBar. 
Visibl--   :=  True; 
    (Устанавливаем диапазон горизонтальной прокрутки.} HorzScrollBar.Range   
:=  Width   *   2; {Показываем горизонтальную полосу прокрутки.} 
HorzScrollBar.Visible   := True; 

    Вы хотите узнать, как получить текущие координаты прокручиваемой области? 
Вспомните главу 6, "Использование компонентов Delphi", вТйУгорой 
рассказывалось о таком свойстве элемента TScrollBar, как Position. 

    Border-Icons 

    Свойство Borderlcons представляет собой набор логических значений, 
использующийся для определения набора пиктограмм в заголовке формы. 
    Значения biMinimize и biMaximize создают пиктограммы, которые позволяют 
свернуть и развернуть форму с помощью мыши. 

    На заметку 
    Для того чтобы значения biMinimize и biMaximize работали, необходимо 
установить свойство Вог

    derStyle равным bsSizeable или bsSizeToolWin. 

    Значение biHelp выводит кнопку с вопросительным знаком. Щелчок на ней 
вызывает контекстно-зависимую справку, которая выводится, как текст подсказки 
Hint, т.е. без вызова Windows Help. На рис. 8.1 представлена такая кнопка. 
    Значение biSystemMenu создает слева от загоповкёГшктогршму, позволяющую 
вызывать системное меню, как показано на рис. 8.2. 

    Рис. 8.1. Доступ к контекстно-зависимой справке можно получить с помощью 
щелчка на кнопке с вопросительным знаком в заголовке формы

    Рис. 8.2. Системное меню позволяет перемещать и закрывать форму, а    также изменять ее размеры 


    214       Часть II. Разработка приложений 


    На заметку 
    Для того чтобы значения biMinimize, biMaximize и biHelp работали, 
необходимо присвоить свойству 

    Borderlcons значениеbiSystemMenu. 
    BorderStyle 

    Свойство BorderStyle пеоечислимого типа позволяет определить: 
    | li   вид заголовка формы;     ! 
    •   доступные кнопки в заголовке формы; 
    •    отображение строки меню; 
    •    поведение границ формы. 

    На рис. 8.3 показана форма для шести значений BorderStyle. Каждая форма 
создавалась как форма размером 200x200 пикселей. 

    Рис. 8.3. Влияние значения BorderStyle на вид формы. Значения по часовой 
стрелке, начиная с верхней левой формы, таковы: bssizeable, bsDialog, 
bsSingle, bsSizeToolWin, bsToolWindow И bsNone 
    По умолчанию свойство BorderStyle имеет значение bsSizeable, создающее 
обычное окно с изменяемыми размерами. Такое окно имеет стандартную строку 
заголовка и не имеет ограничений на расположение в ней кнопок. Примеры таких 
окон — программы Explorer и Notepad. 
    Значение bsDialog создает диалоговое окно, которое используется, когда 
программа требует от вас ответа для продолжения выполнения программы или для 
вывода информации. 16-разрядные версии Windows выводили такое окно как окно с 
широкой границей того же цвета, что и заголовок. Сейчас, если вы будете 
следовать новому трехмерному интерфейсу, границы ок на б^^гв^ля^еть^гак^же, 
как границы обычного окна! Видимое различие между стандартным и диалоговым 
окнами в связи с тем, что последнее не может изменять размеры, состоит лишь в 
том, что указатель мыщи не из.-менд?1ся .прилересечении рамки окна. 

    |На (заметку 
    Значения biMinimize и biMaximize свойства Borderlcons не будут работать, 
если свойство Border-Style установлено равным bsDialog. 

    Третий по популярности стиль окон — bsSingle, создающий форму, которая не 
может изменять размеры во время работы. В отличие от bsDialog, bsSingle не 
запрещает установку любых пикгогрМмГЕдинственнбе'ограничение состоит в том, 
что кнопка сворачивания окна, будучи выведенной, является недоступной 
(блокированной). Пример такой программы — Calculator. 
    На рис. 8.4 вы видите другой пример окна bsSingle. 

    Глава 8. Создание приложений        215 


    Рис. 8.4. Стиль bssingle полезен, когда пользователю не надо изменять 
размер окна 

    Панель инструментов (Toolbar) позволяет быстро получить доступ к 
сгруппированным функциям. В Delphi можно сконструировать панель инструментов, 
поместив группу компонентов TSpeedButton в форму, имеющую стиль bsSizeTocl 
-Win или bsToolWindow. Окно в стиле bsSizeToolWin может изменять размеры и не 
имеет кнопок biMinimize, b: -Maximize и biHelp. Окно в стиле bsToolWindow 
действует так же, но не позволяет изменять размеры. 
    Стиль bsNone создает окно без рамки и заголовка. Такое окно не 
рекомендуется использовать в качестве стандартного или диалогового, однако оно 
может быть полезным в программах сохранения экрана или заставках. На рис. 8.5 
приведен пример такого использования стиля bsNone. 

    На .заметку 
    I 
    На заметку 

    Рис. 8.5. Программы сохранения экрана и заставки обычно ис-t... ,..,.      
 пользуют стиль bsNone 

    Если вы выбрали для свойства BorderStyle значение, создающее окно с 
разрешенным изменением размеров, Delphi автоматически установит значение 
AutoScroll равным True. 
    Формы со стилями bsDialog и bsNone не могут иметь строки меню. 

    Height и Width 

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

    216       Часть II. Разработка приложений 

    {Перемещаем форму в  верхний левый угол  экрана.} Left   :=   0; Тор     
:=   0; 
    {Изменяем размеры формы.} Width     :=   Screen.Width; Height   :=   Screen.Height; 
    (Класс TScreen. о котором будет сказано ниже, и его экземпляр Screen 
предоставляют доступ к информации о размерах всего экрана.) 

    Приведенный код, конечно, работает, но плохо, так как требуется четыре 
обновления формы. На самом деле лучше использовать метод 
SetBoundsГoпpeдeлeнный у потомков 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 и включает строку 
заголовка, меню и рамку окна. Вы можете рисовать в этой~частй окна, перехватив 
сообщение WM_NCPAINT (детально этот процесс описан в главе 4, "Object Pascal: 
углубленное изложение"). 

    FormStyle 

    Свойство FormStyle перечислимого типа определяет, как форма 
взаимодействует с вашим приложением и Windows. Существует два основных стиля 
форм — MDI (Multiple Document Interface — многодокументный интерфейс) и не-MDI. 
    Имеется два MDI-стиля (f sMDIForm и f sMDIChild), которые рассматриваются 
ниже в этой главе. 
    He-MDI формы существуют также в двух вариантах— fsNormal и fsStayOnTop. 
Наиболее популярен стиль f sNormal, который создает стандартный стиль, 
используемый для диалогов, панелей инструментов и SDI-приложений. 

    Рис. 8.6. Стиль f sStayOnTop позволяет форме всегда располагаться поверх 
других окон 

    Стиль f sStayOnTop применяется реже и создает форму, всегда остающуюся 
поверх других форм и приложений, что может быть полезно при выводе системной 
информации и использовании ресурсов. Примером такого окна является окно 
программы Chat, используемой при работе в сети (рис. 8.6). 

    Глава 8. Создание приложений             217 

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

    procedure^ TForml.mnuAlwaysOnTopClick(Sender: TObject); begin . 
    with .mnuAlwaysOnTop do begiii 
    {Переключаем отметку выбора пункта меню.} Qhecked := 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, событие 
сначала пересылается форме, а затем — управляющему элементу, имеющему фокус ввода. 
    Поскольку клавиша <ТаЬ> используется для передачи фокуса другому 
управляющему элементу, она не вызывает генерации события. заметку 
    Обычно свойство используется KeyPreview для обработки функциональных 
клавиш, которые должны быть переданы форме независимо^ от тдкущего активного 
элемента~ ~"~"                                                                 
    Без этого свойства обработка функциональных клавиш сводится к написанию 
для каждого элемента дополнительного обработчика, который должен отслеживать 
нажатие функциональных клавиш. Более элегантным является использование 
разделяемого обработчика событий (речь о нем пойдет ниже в этой главе), так 
что лучше всего применять свойство KeyPressed. 
    При этом все нажатия клавиш отсылаются обработчикам событий OnKeyDown, 
OnKeyUp и OnKeyPress автоматически и для "отлова" функциональной клавиши надо 
написать только один обработчик события OnKeyDown формы. Вот пример закрытия 
формы при нажатии клавиши <F2>. 

    procedure  TForml.FormKeyDown(Sender:   TObject;   var Key:   Word; 
    Shift:     TShiftState): begin 
    {Проверить,   нажата ли  клавиша  <F2>.} if Key = VK_F2  then {Закрыть  
форму.} Close; end; 


    218                 Часть //. Разработка приложений 


    На заметку 
    Когда вы будете использовать этот пример на практике, не забудьте 
присвоить свойству KeyPreview значение True! 

    Рассматривая принцип работы этого кода, имейте в виду, что события 
OnKeyDown и OnKeyUp используют виртуальные коды (все они перечислены в файле 
WINDOWS . PAS). (Об этом говорилось в главе 6, "Использование компонентовТ)еТрЬГТ| 

    Menu 

    Это свойство определяет компонент TMainMenu, который предоставляет главное 
меню формы. Свойство Menu позволяет сделать меню контекстно-зависимым и часто 
используется в OLE-приложениях при замене вашего исходного меню на меню 
приложения-сервера. На рис. 8.7 показан документ Wordpad с внедренным объектом 
MediaClip. Обратите внимание, что меню и панель инструментов при этом те же, 
что и у Media Player. 

    Рис. 8.7. Вы можете создать контекстно-зависимое меню с помощью свойства Menu 

    Для изменения свойства Menu просто присвойте ему новое значение (TMainMenu 
или его потомок): 

    Menu := mnuMainMenu; 
    Position 
    Position — это свойство перечислимого типа, определяющее размещение формы 
при запуске приложения. 

    Значение по умолчанию (poDesigned) заставляет форму выводиться в месте, 
определенном при разработке приложения. Положение и размер формы при этом 
берутся из свойств Left, Top, Height и Width. Поскольку вы не можете знать 
заранее, в какой системе будет запущено ваше приложение, может оказаться, что 
на мониторе с низким разрешением при использовании этого значения свойства 
будет видна только часть формы. 
    Более полезно значение poScreenCenter, использующее заданные вами при 
создании приложения значения Width и Height, но оно так изменяет Left и Тор, 
что форма выводится в центре экрана. 
    Если вы установите Position равным poDefault, Windows автоматически 
установит размеры и положение формы, но вы при этом лишитесь возможности 
контролировать ее размеры. Можете создать форму размером 200x200, которая 
будет выведена как 640x480. Из-за этого, в частности, не допускается 
применение данного значения для MDI-форм. 
    Значение poDef aultPosOnly более полезно, так как оно автоматически 
определяет расположение формы, но не ее размеры (а потому рекомендуется для 
MDI-форм, в которых требуются определенные размеры дочерних форм). 
    Последнее значение cBoficTBaT{poDefauTtSizeOnly) автоматически определяет 
размер, но не расположение формы. Это значение может использоваться там, где 
важно положение формы на экране, а не ее размер. 
    Хотя свойство Position позволяет определить, каким образом будет 
выводиться форма, профессионально сделанные приложения сами запоминают свое 
расположение на экране и при следующем запуске выводятся_в_трй же гюзиции и с 
тем же размером. Это осуществимо, например, благодаря записи положения окна в 
Registry или в INI-файле, в том же каталоге, где нах одится приложение. Обычно 
сохранение позиции и размеров экрана выполняется в самый последний момент — 
при уничтожении формы. 

    procedure TForml.FormDestroy(Sender:   TObject); var 
    sAppPath  :   String; 


    Глава 8. Создание приложений      219 


    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; 

    Ha заметку 
    Для компиляции примера не забудьте включить в раздел uses модуль INIFiles. 
    После выполнения кода ваш INI-файл будет содержать нечто вроде 

    [Forml] 
    Left=108 
    Тор=174 
    Width=540 
    Height=165 

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

    procedure  TForml.Create(Sender:   TObject); const 
    cNOTFOUND  =   -1; var 
    sAppPath   :   String; 
    iniSettings   :   TINIFile;    
    liValue  :   EongTH^T—• 
    begin 
    (Получаем путь   к ЕХЕ-файлу  приложения.} 
    sAppPath   :=  ExtractFilePath(Application.ExeName); 
    {Создаем объект  TINIFile.} 
    iniSettings   :=  TINIFile.Create(sAppPath  +   'SETTINGS.INI1); 
    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); 


    220      Часть II. Разработка приложений 


    Height := iniSettings.Readlnteger(Name, 'Heighr' , Heignti ; Width  := 
iniSettings.Readlnteger(Name,'Width',Width); end; finally 
    iniSettings.Free; end: end; 

    WindowState 

    Свойство перечислимого типа WindowState определяет состояние окна— 
свернутое, развернутое или нормальное. По умолчанию оно имеет значение 
wsNormal (при этом окно выводится в состоянии, определяемом свойствами 
Position, Left, Top, Height и Width). Чтобы свернуть или развернуть форму, 
используются значения wsMinimize и wsMaximize. 

    События TForm 

    Класс TForm добавляет несколько событий к родительскому классу 
TWinControl. Эти события позволяют изменять поведение формы путем выполнения 
загрузки и сохранения информации о состоянии формы или распределения и 
освобождения дополнительных ресурсов. 
    Когда форма создается и отображается, происходит пять следующих событий. 
    1.    OnCreate запускается при создании формы и позволяет распределять 
ресурсы и инициализировать форму. В этой главе, в разделе о свойстве Position, 
был приведен пример инициализации свойств вывода формы в этом событии. 
    2.    OnShow происходит непосредственно перед выводом формы на экран. К 
этому времени все элементы управления и компоненты созданы и инициализированы. 
    Хотя к тому моменту, когда происходит событие OnShow, форма еще не видна, 
свойство Visible установлено равным True. заметку                              
    3. OnResize генерируется при изменении размера формы во время выполнения 
приложения. Обычно здесь помещается код для изменения размера и положения на 
экране элементов управления, не поддерживающих свойство Align. Событие 
OnResize также однократно генерируется при со^даШш°форТлыПс*6гдТВ^1р11^
устанавливает начальные размеры формы. 

OnResize вызывается неоднократно в процессе изменения размеров формы. 
    Нз заметку 

    4.    OnActivate происходит при получении формой фокуса ввода. OnActivate 
вызывается только при переходе фокуса ввода от одной формы к другой в пределах 
одного приложения. При переключении между приложениями Delphi генерирует 
событие OnActivate глобального объекта Application. 
    5.    OnPaint запускается, когда необходимо перерисовать форму. Это может 
происходить, когда форма только что стала видимой, при частичном удалении 
перекрывающих ее элементов или увеличении размеров. Событие полезно, если вы 
перерисовываете какую-то часть формы самостоятельно. 
    Событие OnCreate происходит один раз за все время существования формы, 
прочие же события могут вызываться неоднократно. заметку 

    Глава 8. Создание приложений          221 

    При закрытии и уничтожении формы также генерируется пять следующих событий. 
    1.    OnCloseQuery генерируется в ответ на действия, закрывающие форму. 
Обработчик получает логическую переменную CanClose, определяющую, может ли 
форма быть закрыта. По умолчанию она имеет значение True, но если вы в 
обработчике установите False, форма останется открытой. Обычно это 
используется для сохранения несохраненных файлов или для подтверждения закрытия 
формы. Вот пример такого кода. 

    procedure  TForml.FormCloseQuery(Sender:   TObject; 
    var CanClose:   Boolean); begin 
    CanClose   := MessageDlg('Close  form?',   mtConfirmation, 
    [mbYes,mbNo],   0)   = mrYes; end; 

    2.    OnClose генерируется непосредственно перед закрытием формы. Обычно 
оно используется для изменения стандартного поведения формы при закрытии. Для 
этого Delphi передает в обработчик события переменную Action, которая может 
принимать одно из четырех значений: caHide, caMinimize, caNone или caFree. По 
умолчанию для не-MDf-форм используется caHid e, скрывающее форму. Для дочерних 
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, о чем вы можете прочесть в главе 
    2, "Интегрированная среда Delphi". 

    Шаблоны форм 

    Шаблоны форм (Form Templates) предоставляют основу для новой формы. По 
одной заготовке можно создать несколько форм. В Delphi есть хранилище объектов 
(Object Repository), в котором содержится множество различных шаблонов форм, 
диалогов и проектов. 

    222                      Часть П. Разработка приложений 


    Использование шаблонов форм 

    Приведенные ниже опции позволяют использовать шаблоны форм. 
    •   Сору. Эта опция добавляет копию шаблона формы в ваш проект. Изменения 
объекта в проекте не влияют на другие объекты. 
        Use. Эта опция связывает шаблон непосредственно с вашим проектом. 
Изменения в проекте воздействуют на объект, находящийся в хранилище, и наоборот. 
    Для иллюстрации сказанного добавим в проект новую форму, основанную на 
шаблоне About box, следующим образом. 
    1.   Выберите команду File^New Application. Появится пустое приложение. 
    2.   Выберите команду File^New. Появится диалоговое окно New Items. 
    3.   Щелкните на вкладке Forms вверху диалогового окна. Delphi выведет 
доступные шаблоны форм, как показано на рис. 8.8. 
    4.   Убедитесь, что выбран метод Сору или Use. 
    5.   Щелкните на кнопке ОК. Новая форма About box будет добавлена в ваш проект. 
    Если вы выбрали опцию Сору, новая форма будет дубликатом шаблона и 
дальнейшая работа с ней не отразится на шаблоне-оригинале. При использовании 
Use все изменения в одном проекте через хранилище форм будут переданы во все 
проекты^при^е^няющие эту форму с опцией Use. ~ 

    Добавление собственного шаблона 

    Хотя имеющиеся шаблоны весьма полезны и хорошо сделаны, для 
продолжительной профессиональной работы их будет недостаточно. Наверняка нужно 
будет что-либо доработать или создать новую форму, которую можно было бы 
использовать в других приложениях. Чтобы не делать одну и ту же работу дважды 
(трижды, восемьюжды...), создайте шаблон формы и поместите его в хранилище объектов. 
    1.   Создайте форму, добавьте в нее компоненты и приправьте все это кодом 
по вашему вкусу. Конечно, вы вполне можете использовать в качестве заготовки 
имеющийся шаблон. 
    2.   Сохраните форму в папке OB JREPOS Delphi. 

    На заметку 
    Object Repository не хранит копий всех шаблонов, а устанавливает связи 
с_их DFM- и PAS-файлами. Если вы удалите такой фаилГшаолон~станет недееспособным. 
    
    3.   Щелкните правой кнопкой мыши на форме и затем выберите команду Add to 
Repository. При этом Delphi выведет диалоговое окно Add To Repository, 
показанное на рис. 8.9. 
    
    Рис. 8.8. Шаблоны форм позволяют Использовать заготовленные общие формы 

    Рис. 8.9. Используйте диалоговое окно Add To Repository для добавления 
ваших собственных шаблонов в хранилище объектов 

    4.   Из списка Forms выберите форму, которую нужно добавить в хранилище. 
    5.    Введите заголовок формы в поле Title (он будет использоваться в 
качестве подписи под пиктограммой в диалоговом окне New Items). 
    6.    Из раскрывающегося списка Page выберите страницу, на которой будет 
размещен шаблон. 
    7.   Щелкните на кнопке ОК, и форма будет добавлена в Object Repository. 

    Глава А Сяздяя//?/p/waw&VAw          223 

    Delphi автоматически включает вновь созданные формы в текущий проект. Они 
связаны с проектом, как будто вы воспользовались опцией Use. заметку     } 

    Разделяемое хранилище 

    Вам может не понравиться то, что ваши шаблоны содержатся в хранилище 
Delphi. Предположим, что вы деинсталлируете Delphi (при этом, чтобы сохранить 
свои шаблоны, вы будете вынуждены вручную сохранять их в другом месте, а потом 
вновь добавлять их в очередное хранилище). Или, например, вы работаете в 
сетевом окружении и хотите поделиться своими форма ми с другими 
программистами. Обе эти проблемы позволяет решить концепция разделяемого хранилища. 
    Разделяемое хранилище (shared repository) представляет собой папку с двумя 
файлами, используемыми для хранения информации о расположении шаблонов. Вы 
можете определить расположение разделяемого хранилища, выполнив следующие действия. 
    1.    Выберите команду Tools^Options, и на экран будет выведено диалоговое 
окно Environment Options, показанное на рис. 8.10. 
    2.    Щелкните на вкладке Preferences вверху диалогового окна. 
    3.    В поле ввода Directory введите путь к папке, которую вы хотите 
использовать в качестве разделяемого хранилища. 
    4.    Щелкните на ОК. Теперь при выводе диалогового окна New Items Delphi 
будет просматривать эту папку в поисках шаблонов. 
    После того как разделяемое хранилище определено, вы вольны в выборе места 
хранения своих шаблонов. 

    Управление хранилищем объектов 

    После добавления шаблона в хранилище вы вдруг обнаруживаете, что совсем 
забыли внести в него последние исправления. Вы можете сделать это, используя 
диалоговое окно Object Repository, показанное на рис. 8.11. Для его вызова 
воспользуйтесь командой Tools1* Repository. 
    В списке Pages, расположенном слева в диалоговом окне, перечислены 
страницы, выводимые в диалоговом окне New Items. Управлять этим набором можно 
с помощью кнопок Add Page, Delete Page и Rename Page. 

    Рис. 8.10. Опция Shared Repository позволяет определить разделяемое 
хранилище шаблонов 

    Рис. 8.11. Использование диалогового окна Object Repository для работы с шаблонами 

    Выбор страницы из списка приводит к заполнению списка Objects объектами, 
содержащимися на этой странице. Если вы выберете элемент [Object Repository], 
будут показаны все объекты в хранилище. Вы можете перемещать объекты путем их 
перетаскивания из списка Objects на нужную страницу в списке Pages. 
    Кнопка Edit Object позволяет вывести диалоговое окно Edit Object Info, 
показанное на рис. 8.12. С его помощью можно редактировать свойства объекта в 
любой момент. 

    224   Часть II. Разработка приложений 

    Кнопка Delete Object удаляет объект из хранилища, но не удаляет его файлы 
с диска. 
    Переключатели, расположенные внизу диалогового окна, выполняют две задачи. 
Первый переключатель, New Form, позволяет определить шаблон, используемый при 
создании новой формы. Второй, Main Form, определяет шаблон, используемый для 
построения главной формы при создании нового приложения. Для назначения 
соответствующих шаблонов просто выберите объект из списка Objects и отметьте 
нужную опцию. 

    Рис. 8.12. Редактировать свойства объекта можно в хранилище с помощью 
диалогового окна Edit Object Info 

    Если вы выбираете проект, а не форму, диалоговое окно заменяет 
переключатели на новый переключатель New Project. Он позволяет определить 
ша5лон*проекта, используемый при создании нового приложения. 

    Наследование форм 

    Наследование форм воплощает лучшие возможности повторного использования 
форм Use и Сору. При копировании вы создаете дубликат формы и добавляете в 
него необходимые компоненты и код. Неудобство этого метода состоит в том, что 
изменения не вносятся в шаблон. При использовании Use изменения вносятся не 
только в шаблон, но и во все объекты в других пр оектах. 
    Наследование позволяет создать множество экземпляров шаблона, которые 
могут отличаться один от другого, как при использовании Сору. Оно так же 
автт1атинесщ.вно,сит изменения в объект хранилища, как и при использовании Use. 

    Использование наследования форм 

    В хранилище объектов содержится несколько примеров наследования форм. Для 
того чтобы наследовать одну форму из другой, выполните следующие действия. 
    1.    Выберите команду File1* New Application. При этом появится пустое приложение. 
    2.    Выберите команду File^New, и будет выведено диалоговое окно New Items. 
    3.    Щелкните на вкладке Dialogs, и будет выведена страница диалогов. 
    4.    Выберите диалог с пиктограммой Help, кнопки которого выровнены 
вертикально по правой стороне формы. 
    5.                                   Выберите опцию Inherit. 
    6.    Щелкните на кнопке ОК, и Delphi выведет новую диалоговую форму. 
    Заголовок нового диалогового окна— OKHelpRightDlg2. Почему Delphi создает 
это диалоговое окно как OKHel-pRightDlg? Ответ заключается в наследовании 
форм. В списке форм вашего проекта содержится четыре объекта — Forml, 
OKHelpRightDlg, OKHelpRightDlg2 и OKRightDlg. Поскольку вы наследуете новую 
форму из OKHel-pRightDlg, Delphi включает его в ваш про ект для компиляции 
вашей программы. Это и приводит к наименованию новой формы как 
OKHelpRightDlg2. К тому же это позволяет избежать конфликта имен. В свою 
очередь, OKHelpRightDlg — наследник OKRightDlg, а потому последний также 
включен в проект. 

    ПРЕДОСТЕРЕЖЕНИЕ 
    Две poflMTeflbCKMe_^ppMU,,»QKHeipRightDig и OKRightDlg, связываются с 
проектом так же, как и опция Оз^ТтбзтомуГрёшив их изменить! вы 
измените'объекты, хранящиеся в Object Repository. 

    Цепочка наследования отражена в автоматически генерируемом коде. Описание 
класса OKHelpRightDlg2 выглядит так. 

    TOKHelpRightDlg2 = class (TOKHelpRightDlg) private 
    {Закрытые объявления.} public 
    {Открытые объявления.} end; 

    На заметку 
    Описание класса или типа начинается с буквы Т. 


    Глава 8. Создание приложений 225 
    8 Использование Delphi 3. Специальное издание 

    Это объявление не назовешь чересчур информативным. Поскольку вы не 
добавляли ни новых компонентов, ни кода, OKHelpRightDlg2 не определяет ни 
новых свойств, ни методов или событий. Определение класса TOKHelpRightDlg 
несколько интереснее. 

    TOKHelpRightDlg  =  class(   TOKRightDlg   ) 
    HelpBtn:   TButton; 
    procedure  HelpBtnClick(Sender:   TObject); private 
    (Закрытые  объявления.} public 
    {Открытые  объявления.} end; 

    Как вы можете видеть, OKHelpRightDlg получает компоненты и код от 
OKRightDlg и добавляет объект HelpBtn типа TButton, а также обработчик события HelpBtnClick. 

    Преимущества наследования форм. 

    Как уже упоминалось ранее, преимущества наследования форм заключаются в 
возможности добавления новых компонентов и кода в объект и наличии связи с 
оригиналом шаблона в хранилище. Если вы изменяете шаблон, наследуемый объект 
будет автоматически обновлен. Выполните следующие действия. 
    1.    Выберите команду File^New Application, и появится пустое приложение. 
    2.    Закройте главную форму, окно которой озаглавлено Forml. 
    3.    Выберите команду File^New, и будет выведено диалоговое окно New Items. 
    4.    Щелкните на вкладке Dialogs, и будет выведена страница диалогов. 
    5.    Выберите стандартный (Standard) диалог с кнопками, выровненными 
вертикально по правой стороне. 
    6.    Выберите опцию Inherit. 
    7.    Щелкните на кнопке ОК, и Delphi выведет новую диалоговую форму OKRightDlg2. 
    8.    Выберите из меню View=>Forms, и будет выведено диалоговое окно View Form. 
    9.    Выберите шаблонную форму OKRightDlg, щелкните на кнопке ОК, и Delphi 
выведет новую шаблонную форму, озаглавленную Dialog и расположенную точно 
поверх новой формы. 
    Теперь приступим к демонстрации. Переместите шаблонную форму в нижнюю 
часть главного окна Delphi. Вы обратили внимание, что новая форма OKRightDlg2 
не открылась? Это связано с тем, что Delphi обновляет свойства Left и Тор 
одновременно с изменением соответствующих родительских свойств. 
    Однако в обратном направлении изменения не передаются. Чтобы убедиться в 
этом, выполните следующие действия. 
    1.    Выберите команду View^Forms, и появится диалоговое окно View Form. 
    2.    Выберите OKRightDlg2 и щелкните на ОК. 
    3.    Переместите OKRightDlg2 в нижнюю часть экрана. 

    Ваш экран должен выглядеть примерно так, как на рис. 8.13. 

    При перемещении диалога-наследника на экране диалог-родитель будет 
оставаться на месте, что иллюстрирует передачу свойств только в одном 
направлении. Более детально наследование описано в главе 4, "Object Pascal: 
углубленное изложение". 

    Создание приложений SDI 

    Термин SDI (Single Document Interface) дословно означает однодокументный 
интерфейс и описывает приложения, способные загрузить и использовать 
одновременно только один документ. Программа Notepad, приведенная на рис. 
8.14, является ярким представителем такого класса программ. 
    Следует сказать несколько слов о термине документ. Приложения становятся 
все более объектоцентричными, т.е. они работают с неким центральным объектом, 
в который могут быть внедрены внешние объекты. В общем случае эти внешние 
объекты обрабатываются другим специализированным приложением. Примером может 
служить Wordpad (см. рис. 8.7), позволяющий вне дрять любые OLE-объекты в свои 
файлы. Но он остается при этом SDI-приложением, так как может работать только 
с одним объектом (или документом в широком смысле этого слова) Wordpad. 

    226                    Часть II. Разработка приложений 


    Рис. 8.13. Изменения распространяются только в одном направлении — от 
родителей к потомкам 

    Рис. 8.14. Программа Notepad как пример SDI-приложения 

    Способность одновременно работать только с одним объектом не мешает 
приложению использовать дополнительные формы, например диалоговые окна, панели 
инструментов и прочее (на рис. 8.15 показаны панели инструментов в окне 
Wordpad). Для реализации этих возможностей в Delphi просто добавьте форму в 
ваше приложение и установите ее свойство FormStyle рав ным f sSizeToolWin или 
f sToolWindow. 
    Еще одним примером может служить сама Delphi — огромное количество панелей 
инструментов, меню, разнообразных библиотек компонентов, взаимодействующих 
между собой форм... Но в целом она остается SDl-приложением, так как может 
загрузить и использовать одновременно только один объект. 

    Глава 8. Создание приложений       227 


    Рис. 8.15. Программа Wordpad — SDI-приложение с многими формами 


    Пример SDI-приложения 

    Для демонстрации SDI создадим простую программу просмотра изображений. 
    Весь этот проект можно найти на дополнительно распространяемой дискете в 
каталоге EgSDIApp. 

    Построение интерфейса 

    Обычно первым шагом построения программы является создание интерфейса. Не 
будем отступать от традиций и выполним следующие действия. 
    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 
    spEtrrnciSctTLerr" 8 
    spbtnLoad.Top 8 
    spbtnStretch.AllowAllUp True 


    228        Часть II. Разработка приложений 


    spbtnStretch.GroupIndex          1 
    spbtnStretch.Hint                     Stretch 
    spbtnStretch.Left       48 
    spbtnStretch.Top      8 
    spbtnCenter.AllowAHUp   True 
    spbtnCenter.Grouplndex     2 
    spbtnCenter.Hint     Center 
    spbtnCenter.Left     80 
    spbtnCenter.Top     8 

    5.    Поместите еще одну TPanel в форму и установите следующие ее свойства. 
    Свойство Значение 
    Align alClient 
    Caption — 


    6.    Поместите компонент TImage во вновь созданную TPanel и установите 
следующие его свойства. 

    Свойство Значение 

    Align alClient 
    Name imgMain 

    7.    Добавьте в форму TOpenDialog со следующими свойствами. 

    Свойство Значение 

    Filter Bitmaps    (*.bmp) I *.bmp 
    Name opndlgLoad 
    Options [ofPathMustExist,ofFileMustEx 

    Delphi   предоставляет   вам   множество   значков   для   компонента   
TSpeedButton;   они   находятся   в   каталоге IMAGESXBOTTONS. Для нас вполне 
подойдут следующие установки свойств Glyph. 

    Свойство Значение 

    spbtnLoad.Glyph     FLDROPEN.BMP 
    spbtnStretch.Glyph FONTSIZE.BMP 
    spbtnCenter.Glyph PICTURE.BMP 

    Теперь самое время сохранить проект, выбрав в меню команду FileOSave 
Project As. Сохраните Unitl как Main, a проект — как EgSDIApp. 
    Ваша главная форма должна выглядеть так, как показано на рис. 8.16. 

    Рис. 8.16. SDI-приложение обычно состоит из одной главной формы 


    Написание кода 

    Теперь, после создания интерфейса, перейдем к написанию исходного текста 
вашего приложения. Сначала загрузите изображение следующим образом. 
    1.   Дважды щелкните на компоненте spbtnLoad, и Delphi выведет окно 
редактора и автоматически создаст обработчик события OnClick. 

    Глава 8. Создание приложений     229 

    2.   Введите код. 

    if opndlgLoad.Execute  then 
    imgMain.Picture.LoadFromFile(opndlgLoad.FileName); 

    Метод opndlgLoad. Execute вызывает стандартное диалоговое окно открытия 
файла. Если вы выбрали файл л щелкнули на ОК, метод возвращает True и 
загружает в свойство FileName полный путь к имени файла. При щелчке на Cancel 
или нажатии клавиши <Esc> метод вернет False. 
    Компонент TImage предоставляет свойство Picture, которое является 
экземпляром_класса TPicture. Этот класс обеспечивает работу с растровыми 
изображениями,"пиктограммами и~метаф"айлами. Один из его методов^ 
LoadFrorr-File, служит для загрузки изображения по имени файла. 
    Выберите команду Run^Run для компиляции и запуска приложения и попытайтесь 
открыть картинку. Видите, как все просто? 
    Теперь добавьте возможность растягивания изображения. 
    1.   Дважды щелкните на компоненте spbtnStretch, и Delphi выведет окно 
редактора и автоматически создаст обработчик события OnClick. 
    2.    Введите код. 

    imgMain.Stretch   :=   spbtnStretch.Down; 

    Компонент TSpeedButton имеет свойство Down, которое равно True при нажатой 
кнопке. Свойство Stretch класса TImage позволяет растянуть картинку. 
    Для выравнивания картинки по центру воспользуйтесь приведенной выше 
инструкцией (только используйте компонент spbtnCenter) и введите следующий код: 

    imgMain.Center   :=  spbtnCenter.Down; 


    Создание приложения MDI 

    Термин MDI (Multiple Document Interface) дословно означает 
многодокументный интерфейс и описывает приложения, способные загрузить и 
использовать одновременно несколько документов или объектов. Примером такого 
приложения может служить диспетчер файлов (File Manager), показанный на рис. 8.17. 

    Рис. 8.17. MDI-приложение 

    Обычно MDI-приложения состоят минимум из двух форм — родительской и 
дочерней. Свойство родительской формы FormStyle установлено равным f sMDIForm. 
Для дочерней формы установите стиль f sMDIChild. 
    Родительская форма служит контейнером, содержащим дочерние формы, которые 
заключены в клиентскую область и могут перемещаться, изменять размеры, 
минимизироваться или максимизироваться. В вашем приложении могут быть дочерние 
формы разных типов, например одна — для обработки изображений, а другая — для 
работы с текстом. 

    230     Часть П. Разработка приложений 


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

    В MDI-приложении, как правило, требуется выводить несколько экземпляров 
классов формы. Поскольку каждая форма представляет собой объект, она должна 
быть создана перед использованием и освобождена, когда в ней больше не 
нуждаются. Delphi может делать это автоматически, а может предоставить эту 
работу вам. 

    Автоматическое создание форм 

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

    type 
    TForml   =  class    (TForm) 
    private 
    { Закрытые объявления. } 
    public 
    { Открытые объявления. } 
    end; 

    В данном фрагменте кода объявляется класс TForml (подробнее об этом 
рассказывается в главе 4, "Object Pascal: углубленное изложение"). 
    Вторым является место, в котором описывается переменная класса. 

    var Forml: TForml; 

    Здесь описана переменная Forml, указывающая на экземпляр класса TForml и 
доступная из любого модуля. Обычно она используется во время работы программы 
для управления формой. 
    Третье место находится в исходном тексте проекта, доступ к которому можно 
получить с помощью меню View=>Project Source. Этот код выглядит как: 

    Application.CreateForm(TForml,   Forml); 

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

    Динамическое создание форм 

    Хотя автоматическое создание форм полезно при разработке SDI-приложений, 
при создании MDI-приложений оно, как правило, неприемлемо. 
    Для создания нового экземпляра формы используйте конструктор Create класса 
формы. Приведенный ниже код создает новый экземпляр TForml во время работы 
программы и устанавливает его свойство Caption равным 'New Form'. 

    Forml   := TForml.Create(Application); Forml.Caption   :=   'New   Form'; 

    Конструктор Create получает от вас в качестве параметра потомка 
TComponent, который и будет владельцем вашей формы. Обычно в качестве 
владельца выступает Application, чтобы все формы были автоматически закрыты по 
окончании работы приложения. Вы можете также передать параметр Nil, создав 
форму без владельца (или владеющую собой — как вам больше нра витсяУГно"тЗгда 
закрывать и уничтожать ее придется вам. В случае возникновения 
необраба-тв«асяо1ГоШибки такаяф~орма останется в памяти, что не говорит о 
высоком проф"ёссйЪнализме программиста... 
    В приведенном ниже коде Forml указывает только на последнюю созданную 
форму. Если вам это не нравится, воспользуйтесь приведенным ниже кодом — 
возможно, он более точно отвечает вашим запросам: 

    with TForml.Create(Application)   do Caption   :=   'New  Form'; 

    j,     При разработке MDI-приложения метод Show не нужен, так как^ Delphi 
автоматически показывает все. вновь созданные дочерние MDI-формы. В случае 
SDI-приложения вы обязаны использовать метод Show. 

    ЗЭМвТКу 


    Глава 8. Создание приложений       231 

    Даже при динамическом создании форм Delphi попытается навязать вам свои 
услуги по созданию экземпляра_каждой формы. Чтобы отказаться от них, 
воспользуйтесь диалоговым" окном Project Options, изображенным на рис. 8.18, и 
уда-литТКлассы форм из списка Auto-create forms. 

    Рис. 8.18. Диалоговое окно Project Options позволяет установить опции для 
текущего проекта 

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

    MDI-свойства TForm 

    Объект TForm имеет несколько свойств, специфичных для MDI-приложений. 

    ActiveMDIChild 

    Это свойство возвращает дочерний объект TForm, имеющий в текущее время 
фокус ввода. Оно полезно, когда родительская форма содержит панель 
инструментов или меню, команды которых распространяются на открытую дочернюю форму. 
    Например, представим, что проект использует дочернюю форму, содержащую 
элемент ТМето, названный тет-DailyNotes. Имя класса этой дочерней формы — Tf 
rmMDIChild. Родительская форма содержит кнопку Clear в панели инструментов, 
которая удаляет содержимое memDailyNotes в активной дочерней форме. Вот как 
это реализуется. 

    procedure TfrmMDIParent.spbtnClearClick(Sender:   TObject); begin 
    if not   (ActiveMDIChild = Nil)   then 
    if ActiveMDIChild  is  TfrmMDIChild then 
    TfrmMDIChild(ActiveMDIChild).memDailyNotes.Clear; end; T~~' 

    В первой строке проверяется, равен ли AjbtiveMDIChild значению Nil, так 
как в этом случае обращение к объекту вызовет исключительную ситуацию.         
    ActiveMDIChild равен Ni?, если нет открытых дочерних форм или свойство 
FormStyle не равно fsMDIForm. 

    заметку 
    Поскольку ActiveMDIChild возвращает объект TForm, компилятор не имеет 
доступа к memDailyNotes — объекту Tf rmMDIChild. Вторая строка проверят 
соответствие типов, т.е. действительно ли ActiveMDIChild указывает на объект 
Tf rmMDIChild. 

    Третья строка выполняет преобразовани?типа и вызывает метод Clear 
компонента memDailyNotes. 

    232       Часть II. Разработка приложений 


    MDIChildren и MDIChildCount 

    Свойство MDIChildren является массивом объектов TForm, предоставляющих 
доступ к созданным дочерним формам. MDIChildCount возвращает количество 
элементов в массиве MDIChildren. 
    Обычно это свойство используется при выполнении какого-либо действия над 
всеми открытыми дочерними формами. Вот код сворачивания всех дочерних форм 
командой Minimize All. 

    procedure  TForml.mnuMinimizeAllClick(Sender:   TObject); var 
    iCount   :   Integer; begin 
    for  iCount   := MDIChildCount-1  downto  0  do 
    MDIChildren[iCount].WindowState   :=  wsMinimized; end; 

    Если вы будете сворачивать окна в порядке возрастания элементов массива, 
цикл будет работать некорректно, так как после сворачивания каждого окна 
массив MDIChildren обновляется и пересортировывается^ вы можете пропустить 
некоторые элементы. 

    TileMode 

    Это — свойство перечислимого типа, определяющее, как родительская форма 
размещает дочерние при вызове метода Tile. Используются значения tbHorizontal 
(по умолчанию) и tbVertical для размещения форм по горизонтали и вертикали. 
Результат использования этих значений показан на рис. 8.19. 

    Рис. 8.19. Использование свойства TileMode для определения способа 
размещения дочерних окон 


    WindowMenu 

    Профессиональные MDI-приложения позволяют активизировать необходимое 
дочернее окно, выбрав его из списка в меню. Свойство WindowMenu определяет 
объект TMenuItem, который Delphi будет использовать для вывода списка 
доступных дочерних форм. 
    Для вывода списка TMenuItem должно быть меню верхнего уровня. Это меню 
имеет свойство Caption, равное swindow. На рис. 8.20 приведен пример 
использования такого меню. 

    MDI-события TForm 

    В MDI-приложении событие OnActivate запускается только при переключении 
между дочерними формами. Если фокус ввода передается из не-МО1-формы в 
MDI-форму, генерируется событие OnActivate родительской формы, хотя ее 
свойство Active никогда и не устанавливается равным True. Эта странность на 
самом деле строго логична: ведь, если бы OnActivate генерировался только для 
дочерних форм, не было бы никакой возможности узнать о переходе фокуса ввода 
от другого приложения. 

    MDI-методы TForm 

    Специфичные для MDI-форм методы перечислены ниже. 
    И   Arrangelcons выстраивает пиктограммы минимизированных дочерних форм в 
нижней части родительской формы. 
    *   Cascade располагает дочерние формы каскадом, так что видны все их заголовки. 

    Глава 8. Создание приложений    233 

    В   Next и Previous  переходит от одной дочерней формы к другой,  как 
будто вы нажали <Ctrl+Tab> или 
    <Ctrl+Shift+Tab>. В   Tile выстраивает дочерние формы так, что они не 
перекрываются (пример такого размещения показан на 

    рис. 8.20). 

    Рис. 8.20. Использование свойства windowMenu для вывода списка дочерних форм 


    Пример MDI-приложения 

    В этом разделе мы расширим возможности созданной ранее программы просмотра 
изображений. Этот проект находится на дополнительно распространяемой дискете в 
каталоге EgMDIApp. 

    Создание интерфейса 

    Интерфейс MDI-приложения очень похож на интерфейс разработанного ранее 
SDI-приложения, но каждое изображение выводится в отдельной, а не в главной 
форме. Выполните следующие действия для создания родительской формы. 
    1.    Выберите команду File^New Application, и появится пустое приложение. 
    2.   Установите следующие свойства. 

    Свойство Значение 

    Caption Image  Viewer 
    FormStyle fsMDIForm 
    Name -                                                                     
    ShowHint True 

    3.   Поместите компонент TPanel в форму. Установите следующие его свойства. 

    Свойство Значение 

    Align alTop 
    Caption _ 

    4.   Поместите три компонента TSpeedButton в TPanel и назовите их 
spbtnLoad, spbtnStretch и spbtnCenter. Установите следующие их свойства. 

    Свойство Значение 

    spbtnLoad.Hint     Load 
    spbtnLoad.Left     8 


    234         Часть II. Разработка приложений 


    spbtnLoad.Top 8 
    spbtnStretch.AllowAHUp True 
    spbtnStretch.GroupIndex 1 
    spbtnStretch.Hint Stretch 
    spbtnStretch.Left 48 
    spbtnStretch.Top 8 
    spbtnCenter.AllowAllUp 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.   Выберите из меню File1* New Form, и появится пустая форма. 
    2.   Установите следующие ее свойства. 

    Свойство Значение 

    FormStyle fsMDIChild 
    Name TrmMDIChi. 
    Position poDefaultPosOnly 

    3.   Поместите компонент TImage во вновь созданную форму и установите его 
следующие свойства. 

    Свойство Значение 

    Align alClient 
    Name imgMain 

    Удалите дочернюю форму из списка автоматически создаваемых форм следующим образом. 
    1.    Выберите команду Project^Options, и появится диалоговое окно Project 
Options, показанное на рис. 8.18. 
    2.    Выберите f rmMDichild в списке Auto-create forms. 
    3.    Щелкните на кнопке >. Форма f rmMDIChild при этом будет перенесена в 
список Available forms. 
    4.   Щелкните на кнопке ОК. 
    Теперь самое время сохранить проект, выбрав команду File^Save Project As. 
Сохраните Unitl как MDIParent, a проект — как 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; 


    Глава 8. Создание приложений            235 

    После запуска диалогового окна создается новый экземпляр дочерней формы и 
загружается файл изображения. После загрузки размеры дочерней формы изменяются 
так, чтобы можно было видеть все изображение. 
    Еще пара штрихов — и приложение заработает, как и предусматривалось. 
Поскольку модуль ссылается на тип Tf rmMDIChild, находящийся в модуле 
MDIChild, после строки implementation следует добавить еще одну строку: 

    uses MDIChild; 

    Теперь можно приступить к компиляции и запуску приложения. Однако 
заметьте, что, когда вы щелкаете на кнопке Close, дочерняя форма не 
закрывается, а сворачивается в пиктограмму. Чтобы заставить ее закрыться, 
следует добавить в код обработчика OnClose класса Tf rmMDIChild маленькую 
деталь — изменить свойство Action: 

    Action := caFree; 

    Компоненты TSpeedButton Stretch и Center выполняют те же функции, что и в 
SDI-приложении, однако их обработчики события OnClick следует изменить 
следующим образом. 

    if not   (ActiveMDIChild = Nil)   then 
    if ActiveMDIChild  is  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__MD_IParent ; 

Компилируйте, запускайте и смотрите: MDI-приложение создано! 

    ПРЕДОСТЕРЕЖЕНИЕ 
    В этом примере присвоение нового значения свойству Down класса 
TSpeedButton вызывало событие Оп-ciick. Будьте осторожны при написании кода 
обработчика события, который генерирует новое событие путем присвоения 
значения свойству, ведь при этом можно создать бесконечную рекурсию. 

    Создание консольного приложения 

    А теперь забудьте обо всех этих кнопочках, пиктограммах и пропахших мышами 
приложениях — настоящие программисты и пользователи обожают текстовый режим! 
Если же говорить серьезно, иногда программы в текстовом режиме могут быть 
полезными. Примерам таких программ несть числа, и основная проблема всегда 
заключалась в том, что они запускались в окне DO S и не имели доступа к 
Windows API или к таким ресурсам, как принтеры и память (не говоря уже о том, 
что это были 16-разрядные приложения с 640-килобайтовым ограничением на 
используемую память). Теперь, когда Delphi поддерживает создание консольных 
приложений, можно создавать 32-разрядные текстовые приложения с использованием 
линейной модели памят и и с возможностью работать с Windows API и применять ресурсы. 
    Обычно консольные приложения используются, когда не нужно вводить 
информацию большого объема и требования к интерфейсу сведены к минимуму. Ну и 
заметим к тому же, что простенькое "Hello, world!", созданное путем размещения 
компонента TLabel в форме, будет иметь размер около 150 Кбайт, в то время как 
консольное "Hello, world!" уложится в 10... 

    236                             Часть II. Разработка приложений 


    Пример консольного приложения 

    Лучший способ изучить консольные приложения — создать пресловутое "Hello, 
world!". Такой проект есть на дополнительно распространяемой дискете в папке 
EgConsoleHello. Для создания его вручную сделайте следующее. 
    1.    Выберите команду File^New Application. 
    2.    Выберите команду File^Remove From Project, и появится диалоговое 
окно Remove From Project, показанное на рис. 8.21. 
    3.    В проекте содержится один модуль формы. Выберите его и щелкните на 
кнопке ОК. Появится диалоговое окно Save changes to Unitl .pas?. 
    4.    Щелкните на кнопке No, и форма будет удалена из проекта. 
    Сохраните проект как EgConsoleHello. 

    Рис. 8.21. Диалоговое окно Remove From Project позволяет удалять модули и 
формы из проекта 

    Хотя мы создаем "бесформенное" приложение, оно все еще не является 
консольным и истользу^етмШЛ,, а значит, сравнимо по размеру с бронтозавром. 
Выберите команду View1*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 строку WriteLnCHello,   world!'); 
    Сохраните, скомпилируйте и запустите проект из командной строки. Надеюсь, 
вам понравилось? И еще одно замечание — консольные приложения используют 
стандартные потоки ввода-вывода, а значит, вы можете использовать функции 
Read, ReadLn, Write и WriteLn. 

    Функции Windows API для консольного приложения 

    Вы можете делать гораздо больше в консольном приложении, чем просто 
считывать и выводить текст. Вам доступно около 40 функций Windows API для 
работы с дисплеем, например изменение атрибутов выводимых символов или 
изменение размеров консоли. 

    Глава 8. Создание приложений       237 

    В листинге 8.1 изменяется текст, выводимый в заголовке консольного окна. 
Вы можете найти этот пример i дополнительно распространяемой дискете в папке EgConsoleTitle. 

    Листинг 8,1. Изменение заголовка консольного окна 

    program EgConsoleTitle; 
    {$APPTYPE  CONSOLE} 
    uses 
    Windows,   SysUtils; 
    {$RTKE5T~..... 
    sNewTitle, sErrMsg : String; begin 
    sNewTitle  := 'Welcome to Console World'; 
    if not SetConsoleTitle(PChar(sNewTitle)) then 
    begin 
    sErrMsg := 'Unable to set caption - '+SysErrorMessage(GetLastError); 
    MessageBox(0,PChar(sErrMsg),'Error',MB_ICONEXCLAMATION+MB_OK); 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. 
EcлиJЦ?б2Д?Г?r^r^идeда    сертификат Microsoft, то сможете вывести 
логотид^Г2е5]?пе^ fprJ|Vjn95". 
    Для создания нового SDI-приложения с использованием шаблона выполните 
следующие действия. 

    238      Часть II. Разработка приложений 

    1.    Выберите команду File^>New, и появится диалоговое окно New Items 
(см. рис. 8.8). 
    2.    Щелкните на вкладке Projects, что приведет к появлению 
соответствующей страницы. 
    3.    Выберите пиктограмму SDI Application, щелкните на кнопке ОК, и 
появится диалоговое окно Select Directory, показанное на рис. 8.22. 
    4.    Определите каталог для нового проекта. 
    Если вы выберете несуществующий каталог, Delphi создаст его для вас. 

    I Совет ] 

    5.   Щелкните на кнопке ОК, и новый проект будет создан. 
    На заметку 
    При создании проекта доступна только опция Сору. Все файлы проекта 
копируются в ваш каталог и изменения в них не приведут к изменению шаблона 
проекта в хранилище. 
    На странице Projects содержится еще одна пиктограмма — Application Expert. 
Эксперт приложений, показанный на рис. 8.23, проведет вас через все этапы 
создания нового проекта. 

    Рис. 8.22. Диалоговое окно Select Directory позволяет   Рис. 8.23. 
Application Expert поможет вам создать новый проект выбрать каталог для нового проекта 

    На заметку 
    Рекомендуем вам с уважением отнестись к шаблонам проектов, а также к 
проектам, созданным с помощью эксперта приложений — они достойны изучения! 

    Добавление шаблонов проектов 

    Помимо стандартных, вы, вероятно, захотите иметь и собственные шаблоны. 
Для этого добавьте их в Object Repository следующим образом. 
    1.    Создайте проект, добавьте в него код и объекты по вашему усмотрению. 
    2.    Сохраните проект. Вернитесь к разделу "Разделяемое хранилище", чтобы 
вспомнить о том, где и как хранить шаблоны. 
    3.    Выберите команду Projects Add To Repository, после чего будет 
выведено диалоговое окно Add to Repository, показанное на рис. 8.24. 
    4.    Введите запрашиваемую информацию, щелкните на кнопке ОК, и проект 
будет добавлен в Object Repository в качестве шаблона. 

    Рис. 8.24. Диалоговое окно Add to Repository позволяет добавить 
собственный шаблон проекта в Object Repository 

    Глава 8. Создание приложений       239 


    Опции проекта 

    Существует огромное количество опций, которые можно изменять при 
разработке проекта. Они представлены н< вкладках диалогового окна Project 
Options (см. рис. 8.18). 
    Вы всегда можете установить опции по умолчанию, отключив переключатель 
Default в нижнем левом углу диалогового окна Project Options. 
    заметку 

    Forms 

    Раскрывающийся список Main form определяет главную форму вашего 
приложения, которую Delphi выводит первой и закрывает, когда приложение 
завершает работу. 
    Два списка, Auto-create forms и Available forms, позволяют определить 
автоматически создаваемые формы. Этот вопрос уже рассматривался в разделе 
"Создание форм". 

    Application 

    Поле Title определяет название вашего приложения, выводимого в панели 
задач (например, Delphi устанавливает его по имени текущего проекта). Если вы 
не введете названия, будет использоваться название, установленное по умолчанию (DELPHI32). 
    Поле Help file определяет файл справки, подключаемый к вашему приложению, 
что позволяет использовать систему контекстной справки. 
    Кнопка Load Icon позволяет установить пиктограмму приложения, используемую 
как пиктограмму по умолчанию в ярлыках и панели задач. 
    С помощью последней опции, Target file extension, можно переопределить 
стандартное расширение создаваемого файла (DLL — для динамически линкуемых 
библиотек, OCX — для элементов ActiveX и т.п.). Однако это расширение можно не 
устанавливать, так как Delphi весьма корректно работает по умолчанию. 

    Compiler 

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

    Linker 

    Опция Map file полезна для тех программистов, которые интересуются 
технической информацией, например адресами сегментов, стартовым адресом 
программы и т.п. 
    Linker output определяет, что именно будет выдавать компилятор — Delphi 
Compiled Unit (DCU) или объектные файлы (OBJ). Последние могут применяться, 
напримёрТгфи разработке программ с использованием двух языков (читайте главу 
30, "Содружество Delphi и C++"). 
    Опции ЕХЕ и DLL позволяют создавать консольные приложения, описанные в 
разделе "Создание консольного приложения", и включать отладочную информацию, о 
которой подробно рассказывается в главе 27, "Тестирование и отладка". 
    Memory sizes определяет минимальный и максимальный размеры стека 
приложения и устанавливает предпочтительный базовый адрес для вашей DLL. Если 
ваша программа часто использует глубокую рекурсию или большие локальные 
переменные, увеличьте размер стека. Базовый адрес лучше не изменять, если вы 
не совсем уверены в том, что собираетесь делать. 
    EXE Description представляет возможность внести в ЕХЕ или DLL строку 
описания приложения (что-то вроде "MixFix (С) 1993-1997 KIV without Co"). 

    Directories/Conditionals 

    Установки Output и Unit output определяют, где компилятор размещает ЕХЕ 
или DLL, а также скомпилированные модули. Если оставить опции незаполненными, 
создаваемые модули будут располагаться там же, где и исходные тексты, а 
выходные выполняемые файлы или DLL — в папке проекта. 

    240               Часть II. Разработка приложений 

    Conditional defines определяют флаги, проверяемые в процессе компиляции и 
используемые, как правило, для включения или исключения блоков кода из проекта 
при компиляции. 
    Unit Aliases существует для совместимости со старыми версиями Delphi (в 
свое время модуль Windows был разбит на два файла и для сборки одного модуля 
следовало присвоить один и тот же псевдоним Windows обоим файлам). 

    Versionlnfo 

    Вкладка 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 6у^т^скош1от^ны_с_ваштл приложением. Включение опции означает, что ваше 
приложение будет разделять с другими приложениями D elphi одну копию пакетов. 
Более подробно пакеты описаны в главе 15, "Работа с пакетами". 

    Передовые технологии программирования 

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

    Компонент TApplication 

    Компонент TApplication инкапсулирует приложения при выполнении. Delphi 
автоматически создает экземпляр Application класса TApplication при выполнении 
приложения. Для использования этого объекта включите модуль Forms в раздел uses. 

    Свойства TApplication 

    TApplication предоставляет несколько свойств, с помощью которых можно 
отслеживать состояние приложения и контролировать некоторые аспекты его поведения. 

    Active 

    Это свойство возвращает значение True, если в приложении в текущий момент 
содержится фокус ввода. Если Active приложения имеет значение True, то и 
свойство Active для одной из форм также равно True. Обычно оно используется 
для определения, имеет ли приложение фокус ввода, перед выводом строки 
состояния или прорисовкой объекта TCanvas. 
    
    На заметку 

    EXEName 

    Это свойство представляет собой строку, содержащую полный путь 
выполняемого файла (аналог в С — argv [0]). Чаще всего оно используется для 
определения рабочего каталога приложения, в котором могут находиться файлы 
даТнн'ых или инициализацйи'п'рограммы. 

    Глава 8. Создание приложений               241 


    Для получения из EXEName рабочего каталога используйте функцию 
ExtractFilePath или Extract-Совет 1   FileDir из модуля SysUtils. 

    Hint и ShowHint 

    В свойстве _Н1_п±и_соде?жится текст, который будет выведен после запуска 
события OnHint. 
    Свойство ShowHint используется для определения, выводится^™ подсказка "на 
лету". Установка свойства равным False отключает систему подсказок для всех 
элементов в приложёТшй независимо от их индивидуальных установок. Обычно оно 
используется совместно с пунктом меню, включающим или отключающим подсказки. 
Вот примерный код. 

    procedure TForml.mnuToggleHintsClick(Sender:   TObject); begin 
    (Переключение  текущего состояния.} 
    mnuToggleHints.Checked   :=  not mnuToggleHintsChecked; 
    (Обновление  свойства  ShowHint.} 
    Application.ShowHint   := mnuToggleHints.Checked; end; 
    HintColor, HintPause, HintHidePause и HintShortPause 
    HintColor определяет цвет окна всплывающей подсказки, например 
    Application.HintColor   :=  clLime; HintPause и HintHidePause определяют 
временные задержки при показе подсказок следующим образом. 

    1.   Указатель мыши помещается над потомком TControl. 
    Событие OnHint вызывается сразу же после установки указателя мыши на TControl. 

    Нэ заметку 
    2.    Delphi ожидает HintPause миллисекунд перед выводом окна подсказки. 
    3.   Указатель остается над TControl. 
    4.   Delphi ожидает HintHidePause миллисекунд перед закрытием окна подсказки. 
    Свойство HintShowPause определяет задержку перед отображением подсказки, 
если активна подсказка другого элемента. 

    Icon 

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

    Application.Icon.LoadFromFile('INACTIVE.ICO'); 
    Title 

    Определяет заголовок приложения в панели задач. 

    Методы TApplication 
    Minimize и Maximize 

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

    242            Часть II. Разработка приложений 


    ProcessMessages 

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

    while  not Application.Terminated do 

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

    while  not Application.Terminated do Application.ProcessMessages; 

    Программисты часто предпочитают использовать для длительных вычислений 
отдельную подзадачу, реали-зуемую классом TThread. Детальнее об этом 
рассказажэТГглавё~!Я, "Работа с^юкзалтамУ9."""""" 

    Terminate 

    Этот метод — предпочтительный способ закрытия приложения. Terminate не 
закрывает приложение немедленно, а дожидается обработчика события и завершения 
всех других процессов. Обычно оно используется в меню File1* Exit. 

    procedure  TForml.mnuFileExitClick(Sender:   TObject); begin 
    Application.Terminate; end; 


    События TApplication 
    Обработка событий TApplication 

    Поскольку при создании TApplication недоступен, установка его обработчика 
события затруднена из-за невозможности использования инспектора объектов 
(Object Inspector). 
    Вспомните главу 2, "Интегрированная среда Delphi", в которой говорилось о 
том, что фактически свойства событий — это указатели на соответствующие 
функции, вызываемые при запуске событий и обрабатываемые Object Inspector. То, 
что обычно Object Inspector делает автоматически, мы с вами выполним вручную. 
    Сначала опишем обработчик события. Поскольку событие распознается и 
запускается объектом, обработчик должен быть методом объекта. Он также должен 
соответствовать соглашению о вызовах события, которое обычно описано в системе 
справки. Например, из файлов справки можно узнагьГчто" сббЬггиё OnActivIte 
типа TNotifyEvent является наиболее общим типом. 
    Его описание его выглядит так: 

    TNotifyEvent  = procedure   (Sender:   TObject)   of Object; 

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

    type 
    TForml   =  class(TForm) private 
    {Закрытые  объявления.} 
    procedure  OnActivateHandler(Sender:   TObject); public 
    {Открытые  объявления.} end; 

    Описание обработчика как protected не несет особой нагрузки и принято по 
умолчанию. Важно лишь, чтобы он был методом класса. 
    ЗЭМ6 Т Ку 


    Глава 8. Создание приложений       243 

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

    procedure  TForml.OnActivateHandler(Sender:   TObject); begin 
    {Код вашего  обработчика.} end; 

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

    procedure  TForml.FormCreate(Sender:   TObject); begin 
    Application.OnActivate   :=  OnActivateHandler; end; 
    OnActivate и OnDeactivate 

    Эти события оповещают программу об изменении свойства Active. 

    OnException 

    Событие вызывается при необработанной исключительной ситуации. Детально об 
исключительных ситуациях рассказывается в главе 5, "Обработка исключительных ситуаций". 

    OnHint 

    Событие генерируется при перемещении указателя мыши над объектом— потомком 
TControl, если его свойство Hint равно значению, которое отличается от пустой строки. 

    Onldle 

    Событие генерируется, когда приложение ожидает ввода и не занято 
обработкой события. Обычно оно используется для выполнения фоновых задач 
наподобие загрузки базы данных или обработки изображения. 
    Обработчик получает логический параметр Done, по умолчанию равный True. 
Если вы оставляете его равным True. обработчик не запустится до тех пор, пока 
не будет получено и обработано очередное сообщение. Если вы установи -Done 
равным False, обработчик будет запускаться во время ожидания сообщения. 
    Поскольку во время работы обработчика приложение не отрабатывает 
сообщения, делайте его более коротким либо не забывайте запускать из него 
процедуру ProcessMessages. 
    Ниже приведен код использования обработчика для вывода текущего времени в 
объекте TPanel. 

    procedure  TForml.OnldleHandler(Sender:   TObject;   var  Done:   
Boolean); begin 
    pnlTime.Caption   :=  TimeToStr(Now); end; 


    Компонент TScreen 

    Класс TScreen инкапсулирует состояние экрана или выводимой области. Delphi 
во время работы автоматически создает экземпляр класса Screen. Для его 
использования в раздел uses нужно включить модуль Forms. 

    244              Часть II. Разработка приложений 


    Свойства TScreen 
    ActiveControl 

    Это свойство возвращает объект TWinControl, имеющий фокус ввода. Обычно 
оно используется для реализации команд Copy, Cut и Paste для текстовых 
управляющих элементов. Следующий код, помещенный в обработчик TMenuItem, 
выполняет функцию Сору. 

    procedure  TForml.mnuEditCopyClick(Sender:   TObject); begin 
    Screen.ActiveControl.Perform(WM_COPY,0,0); end; 

    Все, что происходит в мире Windows, базируется на сообщениях. Для 
выполнения какой-либо функции управляющие      / элементы часто отсылают^ 
сообщения самим себе. Так и в этом примере управление происходит путем 
передачи сообщения WM_c65y] 

    ActiveForm 

    Это свойство возвращает объект TForm, имеющий фокус ввода. Если приложение 
неактивно, свойство указывает, какая именно форма будет иметь фокус ввода при 
активизации приложения. В качестве примера используем свойство для создания 
мигающего заголовка формы, чтобы привлечь внимание пользователя. Функция 
Windows API, предназначенная для этой цели, дол жна пБлЗ'читТдёскриптор окна: 

    FlashWindow(Screen.ActiveForm.Handle,False); 


    Cursor 

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

    {Придать   курсору форму песочных часов.} Screen.Cursor   :=  crHourglass; 
    try 
    {Попытаемся ничего не делать какое-то время.} for iCount :=1 to 1000000000 do; 
    finally 
    (Восстановим форму указателя.) 
    Screen.Cursor   :=  crDefault; end; 

    Форму указателя можно изменить для каждого потомка TControl (включая 
TForm) отдельно. На 

    заметку 

    Forms и FormCount 

    Эти свойства возвращают список форм и их количество. Работа с ними ничем 
не отличается от работы со списком дочерних окон, описанном в разделе 
"MDIChildren и MDIChildCount". 

    Height и Width 

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

    Left   :=   (Screen.Width  -  Width)   div  2; Top   :=   (Screen.Height  
-  Height)   div 2; 


    События TScreen 

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

    Глава 8. Создание приложений       245 


    OnActiveControlChange 

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

    procedure TForml.ActiveControlChangeHandler(Sender:   TObject); begin 
    if   (not  Application.Terminated)   then 
    pnlStatus.Caption   := ActiveControl.Hint; end; 

    Проверка связана с тем, что событие генерируется и при закрытии 
приложения, а при этом обращение к уже уничтоженному объекту вызовет 
исключительную ситуацию.'                                                      
                                                                               
    OnActiveFormChange 

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

    procedure  TMainForm.UpdateMenuItems(Sender:   TObject); begin 
    FileCloseltem.Enabled     :=  MDIChildCount   >   0; 
    FileSaveltem.Enabled        :=  MDIChildCount  >   0; 
    FileSaveAsItem.Enabled   := MDIChildCount   >   0; 

    Процедура UpdateMenuItems назначена в качестве обработчика событию 
OnActiveFormChange в обработчике 

    OnCreate. 
    procedure TMainForm.FormCreate(Sender: TObject); begin 
    Application.OnHint := ShowHint; 
    Screen.OnActiveFormChange := UpdateMenuItems; end; 


    Разделяемые обработчики событий 

    Как вы уже знаете, каждый класс способен генерировать свои собственные 
события. Каждое из них имеет определен-ный_тип, как, например, TNotifyEvent у 
Onclick и TCloseEvent у OnClose. ЙёТрги позволяет написать один ои-работчик 
события и назначить его нескольким событиям одновременно. 
    Представьте себе объект TEdit, генерирующий события OnKeyDown и OnKeyUp. 
Поскольку оба события — одною типа, можете написать одну процедуру и назначить 
ее обоим событиям. Процедура будет вызываться дважды при каждом нажатии 
клавиши (при нажатии и отпускании). Или, например, вы можете создать один 
обработчик для событий OnCreate и OnClick. 
    Еще одно общее назначение разделяемых обработчиков — обработка событий от 
двух различных управляющих элементов, которые могут и не быть экземплярами 
одного класса. 
    Вот как создать разделяемый между классами TButton и TEdit обработчик OnClick. 
    1.    Выберите из меню File^New Application для создания приложения. 
    2.   Поместите TButton в форму и введите в обработчик OnClick следующий код. 
    procedure  TForml.ButtonlClick(Sender:   TObject); begin 

    Editl.SetFocus; 
    Editl.SelectAll; end; 

    3.   Поместите TEdit в форму. В Object Inspector выберите в списке для 
события OnClick обработчик ButtonlClick. 
    Теперь после щелчка на кнопке и на объекте TEdit будут выполняться одни и 
те же действия — фокус ввода будет передаваться управляющему элементу TEdit и 
весь текст в нем будет выделяться. 

    246               Часть II. Разработка приложений 


    Параметр Sender 

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

    Создание одноэкземплярного приложения 

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

    Поиск по заголовкам окон 

    При создании экземпляра окна Windows требует зарегистрировать имя класса 
окна (window class name). Delphi использует класс формы в качестве имени 
класса окна, например, когда Delphi создает экземпляр Forml класса TForml, 
TForml используется в качестве имени для регистрации окна Forml. Каждая форма 
имеет свойство Caption, известное как заголовок окна (window title). Эти два 
параметра позволяют искать окно с помощью функции Windows API FindWin-dow, 
возвращающей дескриптор найденного окна. 
    Эту функцию нельзя вызывать из класса TForml, так как к тому времени окно 
уже будет создано и вы окажетесь в глупом положении, найдя сами себя. Вот код, 
который должен находиться в файле проекта. 

    begin 
    Application. Initialize/
    if FindWindow('TForml','Forml'; Application.Terminate; 
    <>   0   then 
    Application.CreateForm(TForml,Forml); Application.Run; end. 

    Поскольку вы используете функцию Windows API, проследите, чтобы был 
подключен модуль Windows. 

    На заметку 
    Если вы запускаете это приложение из Delphi, учтите, что Form Designer уже 
создал такое окно и вы всегда сможете его найти. Это приложение следует 
запускать отдельно, закрыв Delphi. 

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

    Активизирование существующей копии 

    Все-таки, сказать пользователю "Ты уже запустил одну копию, теперь иди и 
ищи ее!"— как-то негуманно... Более профессиональным решением будет 
активизировать существующую копию с помощью другой функции Windows API — 
SetForegroundWindow. Измените проект следующим образом. 

    var 
    hwndPrev : HWND; begin 
    Application.Initialize; 
    hwndPrev := FindWindow('TForml','Forml'); 
    if hwndPrev <> 0 then 
    begin 
    SetForegroundWindow(hwndPrev);  л 
    Application.Terminate;         ? end,'   ~"  ""   ""    — 
    end. 
    Application.CreateForm(TForml,Forml); Application.Run; 


    Глава 8. Создание приложений        247 


    Резюме 

    В этой главе был детально описан самый распространенный элемент программы 
Delphi — класс TForm. Здесь рассматривались его свойства, методы и события, а 
также возможности его повторного использования с помощью опцив Object Repository. 
    Затем речь шла о трех типах приложений — SDI, MDI и консольных; причем 
большое внимание уделялось специфике разработки MDI-приложений и использованию 
Windows API в консольных приложениях. Кроме того, вы узнали, как создавать 
шаблоны проектов и помещать их в Object Repository для повторного использования. 
    Кроме того, в настоящей главе рассказывалось о классах TApplication и 
TScreen и назначении обработчиков событий объектам Application и Screen. 
    И, наконец, последняя тема главы — разделяемые обработчики событий и 
создание одноэкземплярного приложения с использованием функций Windows API. 

    248     Часть II. Разработка приложений 

