Вы находитесь на странице: 1из 464

Никита Культин

Санкт-Петербург
«БХВ-Петербург»
2008
УДК 681.3.068+800.92С++
ББК 32.973.26-018.1
К90

Культин Н. Б.
К90 C++ Builder. — 2-е изд., перераб. и доп. — СПб.: БХВ-Петербург,
2008. — 464 с.: ил. + CD-ROM — (Самоучитель)
ISBN 978-5-9775-0268-9
Книга представляет собой пособие по программированию в С++ Builder,
в ней подробно рассмотрен процесс создания программы: от разработки
диалогового окна и функций обработки событий до создания справочной
системы и образа установочного компакт-диска. Изложены принципы визу-
ального проектирования и событийного программирования, на конкретных
примерах показаны возможности среды разработки, назначение компонен-
тов, методика создания программ различного назначения. Рассмотрены во-
просы программирования графики, анимации, мультимедиа, разработки
программ работы с базами данных. В приложении приведено описание ба-
зовых компонентов и наиболее часто используемых функций. Книга адре-
сована студентам, школьникам старших классов и всем, кто хочет научить-
ся программировать в С++ Builder.
Компакт-диск содержит проекты, приведенные в книге в качестве при-
меров.
Для начинающих программистов
УДК 681.3.068+800.92С++
ББК 32.973.26-018.1
Группа подготовки издания:
Главный редактор Екатерина Кондукова
Зам. главного редактора Игорь Шишигин
Зав. редакцией Григорий Добин
Редактор Римма Смоляк
Компьютерная верстка Ольги Сергиенко
Корректор Зинаида Дмитриева
Дизайн серии Инны Тачиной
Оформление обложки Елены Беляевой
Зав. производством Николай Тверских

Лицензия ИД № 02429 от 24.07.00. Подписано в печать 30.05.08.


Формат 70 1001/16. Печать офсетная. Усл. печ. л. 37,41.
Тираж 3000 экз. Заказ №
"ÁÕÂ-Петербург", 194354, Санкт-Петербург, ул. Есенина, 5Б.
Отпечатано с готовых диапозитивов
в ГУП "Типография "Наука"
199034, Санкт-Петербург, 9 линия, 12

ISBN 978-5-9775-0268-9 © Культин Н. Б., 2008


© Оформление, издательство "БХВ-Петербург", 2008
Îãëàâëåíèå

Предисловие ........................................................................................................... 9
С++ Builder — что это? ....................................................................................................... 9
Об этой книге ..................................................................................................................... 10

ЧАСТЬ I. СРЕДА РАЗРАБОТКИ C++ BUILDER ......................................... 13


Глава 1. Начало работы ..................................................................................... 14
Установка............................................................................................................................ 14
Активация ........................................................................................................................... 15
Первое знакомство ............................................................................................................. 15

Глава 2. Первый проект .................................................................................... 20


Начало работы .................................................................................................................... 20
Форма .................................................................................................................................. 21
Компоненты ........................................................................................................................ 26
Событие и функция обработки события .......................................................................... 36
Редактор кода ..................................................................................................................... 41
Система подсказок ..................................................................................................... 41
Шаблоны кода ............................................................................................................ 43
Справочная информация ................................................................................................... 43
Сохранение проекта ........................................................................................................... 44
Структура проекта.............................................................................................................. 45
Компиляция ................................................................................................................ 49
Ошибки ................................................................................................................... 51
Предупреждения и подсказки ............................................................................... 52
Компоновка................................................................................................................. 53
Запуск программы ...................................................................................................... 55
Исключения ................................................................................................................ 55
Обработка исключений .............................................................................................. 56
Внесение изменений .................................................................................................. 60
Настройка приложения ...................................................................................................... 66
Завершение проекта ........................................................................................................... 68
Установка приложения на другой компьютер ................................................................. 68
4 Îãëàâëåíèå

Глава 3. Базовые компоненты .......................................................................... 70


Label .................................................................................................................................... 70
Edit ..................................................................................................................................... 73
Button ................................................................................................................................... 76
CheckBox ............................................................................................................................. 79
RadioButton ......................................................................................................................... 82
ComboBox............................................................................................................................ 85
ListBox ................................................................................................................................. 88
Memo ................................................................................................................................... 93
Timer .................................................................................................................................... 96
Panel .................................................................................................................................... 98
ControlBar ......................................................................................................................... 100
SpeedButton ....................................................................................................................... 101
StatusBar............................................................................................................................ 105
UpDown ............................................................................................................................. 108
TrayIcon ............................................................................................................................. 112
ProgressBar ....................................................................................................................... 115
Image ................................................................................................................................. 118
MainMenu .......................................................................................................................... 124
PopupMenu ........................................................................................................................ 132
OpenDialog........................................................................................................................ 133
SaveDialog ......................................................................................................................... 137

ЧАСТЬ II. ПРАКТИКУМ ПРОГРАММИРОВАНИЯ ............................... 141


Глава 4. Графика ............................................................................................... 142
Графическая поверхность................................................................................................ 142
Вывод графики ................................................................................................................. 145
Перо и кисть ..................................................................................................................... 145
Графические примитивы ................................................................................................. 147
Текст .......................................................................................................................... 148
Линия ......................................................................................................................... 151
Ломаная линия .......................................................................................................... 156
Прямоугольник ......................................................................................................... 157
Многоугольник ......................................................................................................... 160
Окружность и эллипс .......................................................................................... 161
Дуга ....................................................................................................................... 162
Сектор ................................................................................................................... 163
Точка ......................................................................................................................... 163
Битовые образы ................................................................................................................ 164
Загрузка битового образа из файла ......................................................................... 165
Отображение битового образа ................................................................................ 165
Метод Draw .......................................................................................................... 165
Метод CopyRect ................................................................................................... 169
Îãëàâëåíèå 5

Загрузка битового образа из ресурса ...................................................................... 172


Файл ресурсов ...................................................................................................... 173
Подключение файла ресурсов ............................................................................ 174
Загрузка битового образа .................................................................................... 175
Анимация .......................................................................................................................... 175
Движение .................................................................................................................. 176
Использование битовых образов ............................................................................ 180
Взаимодействие с пользователем ........................................................................... 186

Глава 5. Мультимедиа ..................................................................................... 193


Функция PlaySound .......................................................................................................... 193
Компонент MediaPlayer ................................................................................................... 194
Простой MP3-плеер ................................................................................................. 198
Проигрыватель CD ................................................................................................... 203
Воспроизведение MIDI-музыки .............................................................................. 209
Просмотр видеороликов .......................................................................................... 214
Компонент Animate .......................................................................................................... 221

Глава 6. Базы данных ....................................................................................... 225


База данных и СУБД ........................................................................................................ 225
Локальные и удаленные базы данных .................................................................... 225
Структура базы данных ........................................................................................... 226
Механизмы доступа к данным ................................................................................ 227
Компоненты доступа к данным............................................................................... 228
Компоненты отображения данных ......................................................................... 228
База данных Microsoft Access.......................................................................................... 228
Доступ к данным ...................................................................................................... 229
Отображение данных ............................................................................................... 235
Выбор информации из базы данных....................................................................... 240
SQL-запрос ........................................................................................................... 240
Фильтр .................................................................................................................. 245
Работа с базой данных в режиме формы ................................................................ 246
Загрузка строки соединения из ini-файла ............................................................... 254
Сервер InterBase ............................................................................................................... 255
Утилита IBConsole ................................................................................................... 258
Регистрация сервера ............................................................................................ 258
Подключение к серверу....................................................................................... 260
Регистрация пользователя ................................................................................... 261
Регистрация существующей базы данных ......................................................... 261
Соединение с БД .................................................................................................. 262
Создание базы данных ........................................................................................ 263
Создание таблицы ................................................................................................ 264
Ввод данных в таблицу ....................................................................................... 265
Удаление таблицы ............................................................................................... 266
Управление доступом к базе данных ................................................................. 266
6 Îãëàâëåíèå

Утилиты gsec и isql................................................................................................... 267


Защита данных ......................................................................................................... 269
Администратор .................................................................................................... 269
Псевдоним базы данных ..................................................................................... 269
Полномочия пользователя .................................................................................. 270
Сценарии ................................................................................................................... 271
Приложение работы с базой данных InterBase ...................................................... 271
Компоненты dbExpress ............................................................................................ 279
Установка программы работы с БД на другой компьютер .................................. 287
База данных Blackfish SQL .............................................................................................. 287
Установка .................................................................................................................. 288
Доступ к серверу ...................................................................................................... 288
Создание базы данных ............................................................................................. 289
Доступ к базе данных ............................................................................................... 293
Права пользователей ................................................................................................ 294
База данных Книги ................................................................................................... 296
Развертывание приложения работы с базой данных ............................................. 301
Установка и настройка Blackfish SQL................................................................ 301
Установка программы работы с базой данных ................................................. 303

Глава 7. Компонент программиста ............................................................... 305


Модуль компонента ......................................................................................................... 306
Тестирование компонента ............................................................................................... 315
Пакет компонентов .......................................................................................................... 318
Создание пакета........................................................................................................ 319
Компиляция пакета .................................................................................................. 322
Установка пакета ...................................................................................................... 323
Тестирование компонента ............................................................................................... 324
Установка программы на другой компьютер ................................................................ 327
Распространение компонента ......................................................................................... 327

Глава 8. Справочная информация ................................................................ 328


Справочная система HTML Help .................................................................................... 328
Подготовка справочной информации............................................................................. 329
Создание chm-файла ........................................................................................................ 331
Файл проекта ............................................................................................................ 331
Оглавление ................................................................................................................ 332
Идентификаторы разделов ...................................................................................... 336
Компиляция .............................................................................................................. 338
Отображение справочной информации.......................................................................... 338

Глава 9. Создание установочного диска ....................................................... 342


Утилита InstallAware ........................................................................................................ 342
Новый проект ................................................................................................................... 343
Îãëàâëåíèå 7

Общая информация .......................................................................................................... 345


Программа и ее разработчик ................................................................................... 345
Требования к системе .............................................................................................. 346
Компоненты .............................................................................................................. 346
Архитектура ...................................................................................................................... 347
Возможности ............................................................................................................ 347
Файлы ........................................................................................................................ 348
Ярлыки ...................................................................................................................... 349
Интерфейс ......................................................................................................................... 351
Диалоги ..................................................................................................................... 351
Информация о программе и лицензионное соглашение ....................................... 353
Образ установочного диска ............................................................................................. 353

Глава 10. Примеры программ ........................................................................ 356


Экзаменатор...................................................................................................................... 356
Требования к программе ......................................................................................... 357
Файл теста ................................................................................................................. 357
Форма ........................................................................................................................ 360
Отображение иллюстраций ..................................................................................... 361
Доступ к файлу теста ............................................................................................... 361
Текст программы...................................................................................................... 363
Настройка OC ........................................................................................................... 374
Сапер ................................................................................................................................. 375
Правила и представление данных ........................................................................... 375
Форма ........................................................................................................................ 378
Игровое поле............................................................................................................. 379
Начало игры .............................................................................................................. 379
Игра ........................................................................................................................... 382
Справочная информация ......................................................................................... 386
Информация о программе ....................................................................................... 388
Программа ................................................................................................................ 390
MP3-плеер ......................................................................................................................... 399
Форма ........................................................................................................................ 400
Регулятор громкости ................................................................................................ 403
Перемещение окна ................................................................................................... 404
Листинг ..................................................................................................................... 405

Заключение......................................................................................................... 412

ПРИЛОЖЕНИЯ ................................................................................................ 413


Приложение 1. Справочник ............................................................................ 414
Форма ................................................................................................................................ 414
Базовые компоненты........................................................................................................ 415
Animate ...................................................................................................................... 416
Button ......................................................................................................................... 416
8 Îãëàâëåíèå

CheckBox ................................................................................................................... 417


ComboBox .................................................................................................................. 418
Edit ............................................................................................................................. 419
Image ......................................................................................................................... 420
Label .......................................................................................................................... 421
ListBox ....................................................................................................................... 422
MediaPlayer ............................................................................................................... 423
Memo.......................................................................................................................... 424
OpenDialog ................................................................................................................ 425
RadioButton ............................................................................................................... 426
SaveDialog ................................................................................................................. 427
SpeedButton ............................................................................................................... 428
StringGrid .................................................................................................................. 429
Timer .......................................................................................................................... 431
UpDown ..................................................................................................................... 431
Компоненты доступа/манипулирования данными ........................................................ 432
ADOConnection ......................................................................................................... 432
ADODataSet .............................................................................................................. 433
ADOQuery ................................................................................................................. 434
ADOTable .................................................................................................................. 435
DataSource ................................................................................................................ 435
DBEdit, DBMemo, DBText ........................................................................................ 436
DBGrid ....................................................................................................................... 437
DBNavigator .............................................................................................................. 438
Графика ............................................................................................................................. 440
PaintBox..................................................................................................................... 440
Canvas........................................................................................................................ 440
Brush .......................................................................................................................... 442
Pen ............................................................................................................................. 443
Цвет ........................................................................................................................... 444
Функции ............................................................................................................................ 445
Функции ввода и вывода ......................................................................................... 445
Математические функции........................................................................................ 446
Функции преобразования ........................................................................................ 446
Функции манипулирования строками .................................................................... 447
Функции манипулирования датами и временем .................................................... 448
События ............................................................................................................................ 451
Исключения ...................................................................................................................... 451

Приложение 2. Описание компакт-диска..................................................... 453

Рекомендуемая литература ............................................................................. 458

Предметный указатель .................................................................................... 459


Ïðåäèñëîâèå

Ñ++ Builder — ÷òî ýòî?


Среда разработки Borland C++ Builder является одним из популярнейших ин-
струментов разработки прикладных программ (приложений) различного на-
значения. Она ориентирована на так называемую "быструю" разработку, в
основе которой лежит технология визуального проектирования и событийно-
го программирования. Суть этой технологии в том, что среда разработки бе-
рет на себя большую часть рутины, оставляя программисту работу по созда-
нию диалоговых окон и процедур обработки событий. Производительность
программиста при этом просто фантастическая!
C++ Builder — это среда разработки приложений (RAD-среда, от Rapid
Application Development — быстрая разработка приложений) на языке С++.
Среда является интегрированной, т. е. объединяет в себе специализирован-
ный редактор кода (текста программ), высокопроизводительный компилятор
и отладчик. Следует обратить внимание на то, что хотя среда разработки
C++ Builder объектно-ориентированная, тем не менее для работы в ней не
требуется фундаментальных знаний в области объектно-ориентированного
программирования — достаточно понимания концепции.
Изначально, вплоть до шестой версии, C++ Builder существовала как само-
стоятельное средство разработки. Затем вместе с другими инструментами
Borland (Delphi и C# Builder) она была интегрирована в Borland Developer
Studio. Теперь после реорганизации Borland Corporation и создания в ее
структуре нового подразделения CodeGear разработчикам стала доступной
очередная версия C++ Builder — CodeGear C++ Builder 2007.
CodeGear C++ Builder существует в двух вариантах: Professional и Enterprise.
Каждый комплект включает набор инструментов и компонентов, обеспечи-
вающих разработку высокоэффективных приложений различного назначе-
10 Ïðåäèñëîâèå

ния. Возможности Enterprise несколько больше (например, в него включены


драйверы доступа к базам данных Oracle, MS SQL Server, Informix, которых
нет в версии Professional). Помимо среды разработки, в комплект поставки
С++ Builder включен новый сервер баз данных CodeGear Blackfish SQL
Server.
Следует обратить внимание: среда C++ Builder 2007 доступна как отдельный
инструмент разработки, а также как элемент CodeGear RAD Studio 2007. Бо-
лее подробную информацию о продуктах CodeGear можно найти на сайте
фирмы (http://codegear.com).
CodeGear С++ Builder 2007 может работать в среде операционных систем
Microsoft Windows 2000, Microsoft Windows XP Professional, Microsoft
Windows Server 2003, а также в Microsoft Windows Vista. Особых требований,
по современным меркам, к ресурсам компьютера среда разработки не предъ-
являет:
 процессор должен быть класса Pentium III 1,4 ГГц или более мощный (ре-
комендуется Pentium 4 с частотой 2 ГГц + МГц);
 512 Мбайт оперативной памяти (рекомендуется более 1 Гбайт);
 750 Мбайт свободного места на жестком диске, в т. ч. для Microsoft .NET
Framework и Microsoft .NET SDK (среда C++ Builder 2007 является .NET-
приложением).

Îá ýòîé êíèãå
Книга, которую вы держите в руках, — это не описание среды разработ-
ки или языка программирования. Это пособие по программированию в
С++ Builder и по разработке Win32-приложений. В ней представлена техно-
логия визуального проектирования и событийного программирования, на
конкретных примерах рассмотрен процесс создания программы: от разработ-
ки диалогового окна и процедур обработки событий до создания справочной
системы и образа установочного диска.
В первой части приводится краткое описание среды разработки C++ Builder,
вводятся основные понятия и термины технологии визуального проектирова-
ния и событийного программирования, приводится описание базовых компо-
нентов.
Вторая часть посвящена практике программирования. В ней рассматриваются
задачи программирования графики, разработки программ работы с базами
данных, создания компонентов. Уделено внимание вопросам создания спра-
вочной системы и развертывания приложений.
Ïðåäèñëîâèå 11

Цель книги — научить читателя создавать программы различного назначе-


ния: от простых однооконных приложений до программ работы с графикой и
базами данных. Следует обратить внимание на то, что хотя книга ориентиро-
вана на тех, кто обладает начальными знаниями и опытом в программирова-
нии, но тем не менее она вполне доступна и начинающим.
Научиться программировать можно только программируя, решая конкретные
задачи. Поэтому, чтобы получить максимальную пользу от книги, вы должны
работать с ней активно. Изучайте листинги, старайтесь понять, как работают
программы. Не бойтесь экспериментировать, совершенствуйте программы,
вносите в них изменения. Чем больше вы сделаете самостоятельно, тем
большему научитесь!
12 Ïðåäèñëîâèå
часть I

Ñðåäà ðàçðàáîòêè
C++ Builder
Глава 1. Начало работы
Глава 2. Первый проект
Глава 3. Базовые компоненты
ÃËÀÂÀ 1

Íà÷àëî ðàáîòû

Óñòàíîâêà
Установка C++ Builder на компьютер выполняется с DVD-диска, на котором,
помимо C++ Builder, находится сервер баз данных InterBase 2007 (версия
Developer Edition), утилита InstallAware, а также набор .NET-компонентов.
Процесс инсталляции, как правило, активизируется автоматически, после то-
го как установочный диск будет помещен в дисковод.
Следует обратить внимание на то, что C++ Builder является .NET-
приложением. Поэтому установка начинается с проверки наличия на компь-
ютере .NET-компонентов (Microsoft .NET 2.0 Framework, Microsoft .NET 2.0
Framework SDK и Microsoft JSharp Runtime 2.0), необходимых для работы
C++ Builder. Если какого-либо из перечисленных компонентов нет, то он ав-
томатически устанавливается.
Процесс установки C++ Builder обычный. Сначала надо подтвердить согласие
с условиями лицензионного соглашения, ввести серийный номер продукта и
указать компоненты, которые следует установить (точнее — те, которые
устанавливать не надо). Также можно указать диск и каталог, куда следует
установить C++ Builder (по умолчанию C++ Builder 2007 устанавливается
в каталог C:\Program Files\CodeGear\RAD Studio\5.0). По завершении подго-
товительных действий начинается установка.

Помимо C++ Builder на компьютер программиста автоматически устанавливает-


ся сервер баз данных Blackfish SQL Server. Сервер InterBase, утилита
InstallAware по умолчанию на компьютер разработчика не устанавливаются.
Ãëàâà 1. Íà÷àëî ðàáîòû 15

Àêòèâàöèÿ
CodeGear требует активации всех своих продуктов (в том числе trial-версий).
Активация (привязка установленного продукта к конкретному компьютеру и
пользователю) выполняется через Интернет при первом запуске C++ Builder:
после ввода в окне активации серийного номера, имени пользователя и паро-
ля устанавливается соединение с сервером и на компьютер программиста пе-
редается активационный файл.

Ïåðâîå çíàêîìñòâî
Чтобы запустить C++ Builder, надо сделать щелчок на кнопке Пуск и в меню
Все программы выбрать команду CodeGear StudioC++ Builder.
Для того чтобы начать работу над новой программой (Win32-приложением)
или, как принято говорить, проектом, надо в меню File выбрать команду
NewVCL Forms Application — C++ Builder.
Окно C++ Builder в начале работы над новым проектом приведено на
рис. 1.1. В верхней части окна находится строка меню и панели инстру-
ментов.

Рис. 1.1. Окно C++ Builder в начале работы над новым проектом
16 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

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

Рис. 1.2. Окно дизайнера формы

За окном дизайнера формы находится окно редактора кода (рис. 1.3), доступ
к которому можно получить, сделав щелчок на находящемся в нижней части
окна дизайнера формы ярлыке Unit1.cpp (главный модуль формы) или
Unit1.h (заголовочный файл модуля формы) или нажав клавишу <F12>. Для
того чтобы вновь стало доступно окно редактора формы, надо сделать щел-
чок на ярлыке Design или нажать клавишу <F12>.
Слева от окна дизайнера формы находится окно Object Inspector. В верхней
части окна указано имя выбранного в данный момент объекта, на вкладке
Properties (рис. 1.4, а) перечислены свойства объекта, а на вкладке Events
(рис. 1.4, б) — события, на которые объект может реагировать.
Вкладка Properties (свойства) используется для редактирования (изменения)
значений свойств. Свойство — это характеристика объекта (формы, команд-
ной кнопки, поля редактирования и т. д.). Свойства определяют вид объекта,
Ãëàâà 1. Íà÷àëî ðàáîòû 17

Рис. 1.3. Окно редактора кода

его положение относительно других объектов, а также поведение (реакцию


на действия пользователя). Например, свойство Caption формы определяет
текст, который отображается в заголовке формы, а свойства Width и
Height — ее размер (ширину и высоту). Значения свойств указаны в правой
колонке. По умолчанию свойства на вкладке Properties объединены по
функциональному признаку в группы (названия групп выделены цветом).
Так, например, свойства, определяющие внешний вид объекта, объединены в
группу Visual. Программист может изменить способ отображения свойств,
выбрав в контекстном меню вкладки Properties команду Arrangeby Name
(в алфавитном порядке) или Arrangeby Category (по категориям).
На вкладке Events перечислены события, которые может воспринимать объ-
ект (строго говоря, на вкладке Events перечислены свойства, значения кото-
рых определяют реакцию объекта на соответствующие события).
На вкладках окна Tool Palette (рис. 1.5) находятся компоненты.
Компонент — это объект, обеспечивающий решение некоторой задачи.
Например, на вкладке Standard находятся компоненты пользовательского
18 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

интерфейса (Label — поле отображения текста, Button — командная кнопка,


Edit — поле редактирования и др.), а на вкладке dbGo находятся компонен-
ты, обеспечивающие доступ к базам данных.
В окне Project Manager (рис. 1.6) отображается структура проекта, над кото-
рым в данный момент идет работа (в простейшем случае проект образуют
главный модуль (cpp-файл), модуль формы (dfm-, h- и cpp-файлы) и файл ре-
сурсов (res-файл).

а б
Рис. 1.4. В окне Object Inspector на вкладке Properties перечислены свойства объекта,
а на вкладке Events — события, на которые объект может реагировать

Программист может расположить окна так, как ему удобно, может изменить
размер окна или сделать его всплывающим. Перемещение и изменение раз-
мера окна выполняются обычным способом — перетаскиванием области за-
головка (перемещение окна) или границы (изменение размера). Чтобы окно
стало всплывающим (появлялось при позиционировании мыши на его заго-
ловке), надо сделать щелчок на находящейся в его заголовке изображении
канцелярской кнопки.
Ãëàâà 1. Íà÷àëî ðàáîòû 19

Рис. 1.5. Вкладка Standard Рис. 1.6. В окне Project Manager


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

Иногда возникает необходимость восстановить исходное (стандартное) рас-


положение окон. Чтобы это сделать, надо раскрыть список Desktop
speedsetting и выбрать Default Layout (рис. 1.7).

Рис. 1.7. Чтобы восстановить исходное расположение окон,


выберите Default Layout
ÃËÀÂÀ 2

Ïåðâûé ïðîåêò

Процесс разработки программы в C++ Builder рассмотрим на примере — соз-


дадим приложение (так принято называть прикладную программу), с по-
мощью которого можно пересчитать цену из долларов в рубли. Окно про-
граммы приведено на рис. 2.1.

Рис. 2.1. Окно программы Конвертор

Íà÷àëî ðàáîòû
Чтобы начать работу над новым приложением, нужно в меню File выбрать
команду NewVCL Forms Application — C++ Builder. В результате будет
создан новый проект: модуль формы (файлы Unit1.cpp, Unit1.h, Unit1.dfm),
главный модуль (файл Project1.cpp) и файл ресурсов (Project1.res). Форма,
которая отображается в окне дизайнера формы (на вкладке Design) — это ок-
но программы, которое появится на экране в результате запуска программы.
Ãëàâà 2. Ïåðâûé ïðîåêò 21

Ôîðìà
Работа над приложением начинается с настройки стартовой формы путем
изменения значений ее свойств (табл. 2.1).

Òàáëèöà 2.1. Ñâîéñòâà ôîðìû (îáúåêòà TForm)

Свойство Îïèñàíèå

Name Имя (идентификатор) формы. Используется для доступа к фор-


ме, ее свойствам и методам, а также для доступа к компонентам,
которые находятся в форме
Caption Текст заголовка

Width, Ширина формы

Height Высота формы

ClientWidth Ширина внутренней области формы (без учета ширины верти-


кальных границ)
ClientHeight Высота внутренней области формы (без учета ширины горизон-
тальных границ и высоты заголовка)
Position Положение окна в момент первого его появления на экране:
poDesktopCenter — в центре рабочего стола;
poCenterScreen — в центре экрана;
poOwnerFormCenter — в центре родительского окна;
poDesigned — положение окна определяют значения свойств Top
и Left
Top Расстояние от верхней границы формы до верхней границы эк-
рана
Left Расстояние от левой границы формы до левой границы экрана

BorderStyle Вид границы. Граница может быть обычной (bsSizeable), тонкой


(bsSingle) или отсутствовать (bsNone). Если у окна обычная гра-
ница, то во время работы программы пользователь сможет с по-
мощью мыши изменить размер окна. Изменить размер окна с
тонкой границей нельзя. Если граница отсутствует, то на экран во
время работы программы будет выведено окно без заголовка.
Положение и размер такого окна во время работы программы
изменить нельзя
BorderIcons Кнопки управления окном. Значение свойства определяет кнопки
управления окном, которые будут доступны пользователю во
время работы программы.
22 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 2.1 (îêîí÷àíèå)

Свойство Îïèñàíèå

Значение свойства задается путем присвоения значений уточ-


няющим свойствам:
biSystemMenu — определяет доступность кнопки системного меню;
biMinimaze — определяет доступность кнопки Свернуть;
biMaximaze — определяет доступность кнопки Развернуть;
biHelp — определяет доступность кнопки отображения справоч-
ной информации
Icon Значок в заголовке окна

Color Цвет фона. Цвет можно задать, указав название цвета


(clSilver, clWhite и др.) или привязку к текущей цветовой
схеме операционной системы (clBtnFace, clBtnText и др.). По
умолчанию используется второй способ, при котором цвет опре-
деляется текущей цветовой схемой, выбранным компонентом
привязки и меняется при изменении цветовой схемы операцион-
ной системы
Font Шрифт. Шрифт, используемый "по умолчанию" компонентами,
находящимися на поверхности формы. Изменение свойства Font
формы приводит к автоматическому изменению свойства Font
компонента, располагающегося на поверхности формы, т. е. ком-
поненты наследуют свойство Font от формы (имеется возмож-
ность запретить наследование)

Для изменения значений свойств формы (и других объектов) используется


вкладка Properties окна Object Inspector. В левой колонке вкладки перечис-
лены свойства выбранного объекта, в правой — указаны значения его
свойств.
По умолчанию свойства на вкладке Properties объединены в группы по
функциональному признаку (названия групп выделены цветом). Например, в
группу Visual объединены свойства, определяющие вид объекта (для фор-
мы — заголовок, цвет фона, вид границы).
Программист может изменить способ отображения свойств в окне Object
Inspector. Например, чтобы свойства отображались в алфавитном порядке,
в контекстном меню вкладки Properties надо выбрать команду Arrangeby
Name.
Изменить значение свойства можно путем непосредственного редактирова-
ния текущего значения или путем выбора из списка.
Ãëàâà 2. Ïåðâûé ïðîåêò 23

Чтобы изменить текст в заголовке формы, надо изменить значение свойства


Caption. Для этого нужно в окне Object Inspector щелкнуть левой кнопкой
мыши в строке свойства Caption (в результате будет активизирован режим
редактирования значения свойства, появится курсор), ввести текст нового
значения и нажать клавишу <Enter> (рис. 2.2).

Рис. 2.2. Изменение значения свойства Caption путем ввода значения

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


мер, Width и Height, которые определяют размер (ширину и высоту) формы.
Форма — это обычное окно. Поэтому размер формы можно изменить точно
так же, как и любого другого окна, т. е. путем перемещения границы. По
окончании перемещения границы значения свойств Height и Width будут со-
ответствовать установленному размеру формы.
Положение окна на экране в момент его первого появления можно задать,
установив значение свойств Top (отступ от верхней границы экрана) и Left
(отступ от левой границы экрана) или свойства Position.
При выборе отдельных свойств, например BorderStyle, справа от текущего
значения свойства отображается значок раскрывающегося списка. Очевидно,
что значение таких свойств можно задать путем выбора из списка (рис. 2.3).
24 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 2.3. Установка значения Рис. 2.4. Изменение значения уточняющего


свойства путем выбора из списка свойства

Некоторые свойства являются сложными. Например, свойство BorderIcons


задает кнопки управления окном, которые будут доступны во время работы
программы. Значение этого свойства определяется совокупностью значений
свойств biSystemMenu, biMinimize, biMaximize и biHelp, каждое из которых,
в свою очередь, определяет наличие соответствующей командной кнопки в
заголовке окна во время работы программы.
Перед именами сложных свойств стоит значок "+", в результате щелчка кото-
рого раскрывается список уточняющих свойств (рис. 2.4). Значение уточ-
няющего свойства можно задать обычным образом (ввести значение в поле
редактирования или выбрать в списке).
В результате выбора некоторых свойств, например свойства Font, в поле зна-
чения свойства отображается кнопка, на которой видны три точки. Это зна-
чит, что задать значение свойства можно в дополнительном диалоговом окне,
которое появится в результате щелчка на этой кнопке. Например, значение
свойства Font можно задать путем ввода значений уточняющих свойств
(Name, Size, Style и др.), а можно воспользоваться стандартным диалоговым
окном Шрифт, которое появится в результате щелчка на кнопке с тремя точ-
ками (рис. 2.5).
Ãëàâà 2. Ïåðâûé ïðîåêò 25

Рис. 2.5. Чтобы задать свойства Рис. 2.6. Так должна выглядеть форма
шрифта, щелкните на кнопке после ее настройки
с тремя точками

В табл. 2.2 приведены значения свойств стартовой формы программы Кон-


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

Òàáëèöà 2.2. Çíà÷åíèÿ ñâîéñòâ ñòàðòîâîé ôîðìû ïðîãðàììû Êîíâåðòîð

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


Caption Конвертор

Width 360

Height 245

BorderStyle bsSingle Тонкая граница. Во время работы про-


граммы пользователь не сможет из-
менить размер окна путем перемеще-
ния его границы
26 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 2.2 (îêîí÷àíèå)

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


BorderIcons.biMaximize false В заголовке окна не отображать кноп-
ку Развернуть. Во время работы про-
граммы пользователь не сможет раз-
вернуть окно на весь экран
Font.Name Tahoma
Font.Size 9
Position poDesktopCenter В момент запуска программы окно
появится в центре рабочего стола

Êîìïîíåíòû
Различают два типа компонентов: визуальные и невизуальные.
Визуальными называют компоненты, отображаемые в окне программы во
время ее работы. Поле редактирования (Edit), поле отображения текста
(Label), кнопка (Butoon), список (ListBox), поле отображения иллюстрации
(Image) — все это примеры визуальных компонентов. Имеется библиотека
визуальных компонентов — Visual Component Library. Именно поэтому при-
ложения, использующие VCL-компоненты, называют VCL-приложениями.
Невизуальные компоненты отображаются только на форме во время разра-
ботки программы. Таймер (Timer) — типичный невизуальный компонент.
Компоненты, которые программист может использовать при разработке про-
грамм, находятся на вкладках палитры компонентов (окно Tool Palette). На
вкладках Standard, Additional и Win32 находятся компоненты пользователь-
ского интерфейса. Вкладка Dialogs содержит компоненты, обеспечивающие
отображение стандартных диалогов (Открыть, Сохранить и др.). Вкладки
Data Access, Data Controls, dbExpress, dbGo содержат компоненты, обеспе-
чивающие работу с базами данных.
Рассматриваемая нами программа пересчета цены из долларов в рубли долж-
на получить от пользователя исходные данные: цену в долларах и курс. Ввод
данных с клавиатуры обеспечивает компонент Edit. Поэтому в форму разра-
батываемого приложения нужно добавить два компонента Edit.
Для того чтобы в форму добавить компонент Edit, надо:
1. В палитре компонентов раскрыть вкладку Standard.
2. Сделать щелчок левой кнопкой мыши на значке компонента Edit
(рис. 2.7).
Ãëàâà 2. Ïåðâûé ïðîåêò 27

3. Сделать щелчок левой кнопкой мыши в той точке формы, в которой дол-
жен быть левый верхний угол компонента.
В результате выполнения описанных выше действий на форме должен по-
явиться компонент Edit — поле редактирования (рис. 2.8).

Рис. 2.7. Компонент Edit — поле редактирования

Рис. 2.8. Результат добавления на форму компонента Edit


28 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Каждому добавленному программистом компоненту среда разработки при-


сваивает имя, которое состоит из названия компонента и порядкового номера.
Например, первый компонент Edit получает имя Edit1, второй — Edit2
и т. д. Программист путем изменения значения свойства Name может изменить
имя компонента. Однако в простых программах имена компонентов, как пра-
вило, не изменяют.
Основные свойства компонента Edit приведены в табл. 2.3.

Òàáëèöà 2.3. Ñâîéñòâà êîìïîíåíòà Edit

Свойство Описание
Name Имя (идентификатор) компонента
Text Текст, который находится в поле редактирования
Left Расстояние от левой границы компонента до левой границы
формы
Top Расстояние от верхней границы компонента до верхней гра-
ницы формы
Height Высота компонента
Width Ширина компонента
Font Шрифт, используемый для отображения текста в поле ком-
понента
ParentFont Признак наследования шрифта от формы. Если значения
свойства равно true, то для отображения текста в поле
компонента используется шрифт формы
MaxLength Количество символов, которое можно ввести в поле редак-
тирования. Если значение свойства равно нулю, ограниче-
ния на количество символов нет

На рис. 2.9 приведен вид формы после добавления двух полей редактирова-
ния. Один из компонентов выбран (выделен), помечен восьмью маленькими
кружками. Свойства выбранного компонента отображаются в окне Object
Inspector. Чтобы увидеть и, если надо, изменить свойства другого компонен-
та, нужно этот компонент выбрать — щелкнуть левой кнопкой мыши на изо-
бражении компонента или выбрать имя компонента в раскрывающемся спи-
ске, который находится в верхней части окна Object Inspector (рис. 2.10).
Компонент, свойства которого надо изменить, можно выбрать и в окне
Structure (рис. 2.11).
Значения свойств, определяющих размер и положение компонента на по-
верхности формы, можно изменить с помощью мыши.
Ãëàâà 2. Ïåðâûé ïðîåêò 29

Рис. 2.9. Форма с двумя компонентами

Рис. 2.10. Выбор компонента Рис. 2.11. Выбор компонента


в окне Object Inspector в окне Structure

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


курсор мыши на его изображение, нажать левую кнопку мыши и, удерживая
ее нажатой, переместить компонент в нужную точку формы. Во время пере-
мещения компонента (рис. 2.12) отображаются текущие значения координат
левого верхнего угла компонента (значения свойств Left и Top).
30 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 2.12. Отображение значений свойств Left и Тор


при изменении положения компонента

Для того чтобы изменить размер компонента, необходимо сделать щелчок на


его изображении (в результате чего компонент будет выделен), установить
указатель мыши на один из маркеров, помечающих границу компонента, на-
жать левую кнопку мыши и, удерживая ее нажатой, изменить положение гра-
ницы компонента. Во время изменения размера компонента отображаются
его текущие размеры: ширина и высота (значения свойств Width и Height)
(рис. 2.13).

Рис. 2.13. Отображение значений свойств Width и Height


при изменении размера компонента

В табл. 2.4 приведены значения свойств компонентов Edit1 и Edit2 формы


разрабатываемого приложения (значения остальных свойств оставлены без
изменения и поэтому в таблице не приведены).
Компонент Edit1 предназначен для ввода курса, Edit2 — для ввода цены.
Обратите внимание на то, что значением свойства Text обоих компонентов
является пустая строка. Так как значения свойства Font обоих компонентов
Ãëàâà 2. Ïåðâûé ïðîåêò 31

Edit не указаны явно, то во время работы программы текст в полях редакти-


рования будет отображаться шрифтом, заданным свойством Font формы.
Компонент Edit, как и другие компоненты, находящиеся на форме, наследует
значение свойства Font от своего родителя — объекта, на поверхности кото-
рого он находится. Поэтому если изменить значение свойства Font формы,
то автоматически изменится значение свойства Font компонентов, находя-
щихся на форме. Если надо чтобы текст в поле компонента отображался дру-
гим шрифтом, то нужно явно задать значение свойства Font этого компонен-
та. Чтобы запретить автоматическое изменение значения свойства Font ком-
понента при изменении свойства Font формы, надо свойству ParentFont
компонента присвоить значение false.
Форма программы Конвертор после настройки компонентов Edit приведена
на рис. 2.14.

Òàáëèöà 2.4. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ Edit1 è Edit2

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


Edit1 Left 64
Top 60
Text
Edit2 Left 64
Top 92
Text

Рис. 2.14. Форма после настройки компонентов Edit


32 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Отображение текста на поверхности формы обеспечивает компонент Label.


В рассматриваемой программе текст отображается в верхней части окна
(краткая инструкция) и слева от полей редактирования (информация о назна-
чении полей ввода). Результат расчета также отображается в окне программы.
Поэтому в форму надо добавить четыре компонента Label.
Добавляется на форму компонент Label, его значок находится на вкладке
Standard (рис. 2.15) — точно так же, как и поле редактирования (компонент
Edit).
Основные свойства компонента Label приведены в табл. 2.5.

Рис. 2.15. Компонент Label — поле отображения текста

Òàáëèöà 2.5. Ñâîéñòâà êîìïîíåíòà Label

Свойство Определяет
Name Имя (идентификатор) компонента
Caption Отображаемый текст
Font Шрифт, используемый для отображения текста
ParentFont Признак наследования шрифта
Ãëàâà 2. Ïåðâûé ïðîåêò 33

Òàáëèöà 2.5 (îêîí÷àíèå)

Свойство Определяет
AutoSize Признак того, что размер поля определяется его содержимым
Left Расстояние от левой границы поля вывода до левой границы формы
Top Расстояние от верхней границы поля вывода до верхней границы
формы
Height Высота поля вывода
Width Ширина поля вывода
WordWrap Признак того, что слова, которые не помещаются в текущей строке,
необходимо перенести на следующую строку (значение свойства
AutoSize должно быть false)

Если поле Label должно содержать несколько строк текста, то перед тем как
ввести в поле текст (изменить значение свойства Caption), надо присвоить
свойству AutoSize значение false, а свойству WordWrap — true. Потом нужно
установить требуемый размер поля (с помощью мыши или вводом значений
свойств Width и Height) и только после этого ввести значение свойства
Caption.
В форму разрабатываемого приложения надо добавить четыре компонента
Label. Поле Label1 предназначено для вывода информационного сообщения,
поля Label2 и Label3 — для вывода информации о назначении полей ввода,
поле Label4 — для вывода результата расчета — цены в рублях. Значения
свойств компонентов Label приведены в табл. 2.6. После настройки компо-
нентов форма разрабатываемого приложения должна выглядеть так, как по-
казано на рис. 2.16.

Òàáëèöà 2.6. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ Label

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


Label1 Left 16
Top 16
AutoSize false
WordWrap true
Width 297
Height 33
Font.Bold true
Caption Введите курс, цену в долларах и щелкните на кнопке
Пересчет
34 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 2.6 (îêîí÷àíèå)

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


Label2 Left 16
Top 70
Caption Курс
Label3 Left 16
Top 100
Caption Цена
Label4 Left 16
Top 186
AutoSuze false
Width 297
Height 24
Caption

Рис. 2.16. Вид формы после настройки полей отображения текста

Последнее, что надо сделать на этапе создания формы, — это добавить в


форму две командные кнопки: Пересчет и Завершить. Назначение этих кно-
пок очевидно.
Командная кнопка (компонент Button) добавляется на форму точно так же,
как и другие компоненты. Значок компонента Button находится на вкладке
Standard (рис. 2.17). Основные свойства компонента Button приведены
в табл. 2.7.
Ãëàâà 2. Ïåðâûé ïðîåêò 35

Рис. 2.17. Командная кнопка — компонент Button

Òàáëèöà 2.7. Ñâîéñòâà êîìïîíåíòà Button

Свойство Îïèñàíèå

Name Имя (идентификатор) компонента


Caption Текст на кнопке
Enabled Признак доступности кнопки. Кнопка доступна, если значение свой-
ства равно true, и не доступна, если значение свойства равно false

Left Расстояние от левой границы кнопки до левой границы формы


Top Расстояние от верхней границы кнопки до верхней границы формы
Height Высота кнопки
Width Ширина кнопки

После того как на форму будут добавлены кнопки, нужно выполнить их на-
стройку. Значения свойств компонентов Button приведены в табл. 2.8, окон-
чательный вид формы показан на рис. 2.18.
36 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 2.8. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ Button

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


Button1 Left 16
Top 136
Width 75
Height 25
Caption Пересчет
Button2 Left 110
Top 136
Width 75
Height 25
Caption Завершить

Рис. 2.18. Окончательный вид формы программы Конвертор

Завершив работу по созданию формы, можно приступить к программирова-


нию — созданию процедур обработки событий.

Ñîáûòèå
è ôóíêöèÿ îáðàáîòêè ñîáûòèÿ
Вид созданной формы подсказывает, как работает программа. Очевидно, что
пользователь должен ввести в поля редактирования исходные данные и сде-
лать щелчок на кнопке Пересчет. Щелчок на изображении кнопки — это
пример того, что называется событием.
Ãëàâà 2. Ïåðâûé ïðîåêò 37

Событие (Event) — это то, что происходит во время работы программы.


У каждого события есть имя. Например, щелчок кнопкой мыши — это собы-
тие Click, нажатие клавиши на клавиатуре — событие KeyPress.
В табл. 2.9 приведены некоторые события, возникающие в результате дейст-
вий пользователя.

Òàáëèöà 2.9. Ñîáûòèÿ

Событие Описание

Click Щелчок кнопкой мыши


DblClick Двойной щелчок кнопкой мыши

MouseDown Нажатие кнопки мыши

MouseUp Отпускание кнопки мыши

MouseMove Перемещение мыши

KeyPress Нажатие клавиши

KeyDown Нажатие клавиши. События KeyDown и KeyPress — это чередую-


щиеся, повторяющиеся события, которые происходят до тех пор,
пока не будет отпущена удерживаемая клавиша (в этот момент
происходит событие KeyUp)
KeyUp Отпускание нажатой клавиши

Create Создание объекта (формы, элемента управления). Процедура


обработки этого события обычно используется для инициализа-
ции переменных, выполнения подготовительных действий
Paint Появление окна на экране в начале работы программы, появле-
ние части окна, которая, например, была закрыта другим окном и
в других случаях
Enter Получение элементом управления фокуса (например, перемеще-
ние курсора в поле редактирования)
Exit Потеря элементом управления фокуса (перемещение курсора из
одного поля редактирования в другое)

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


действия пользователя вызывают разные события. Например, щелчок (собы-
тие Click) на кнопке Пересчет и щелчок на кнопке Завершить — это два
разных события.
Реакцией на событие должно быть какое-либо действие. В C++ Builder реак-
ция на событие реализуется как функция обработки события. Таким обра-
38 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

зом, для того чтобы программа в ответ на действия пользователя выполняла


некоторую работу, программист должен написать функцию обработки соот-
ветствующего события.
Процесс создания функции обработки события рассмотрим на примере. Соз-
дадим функцию обработки события Click, которое возникает в результате
щелчка на кнопке Пересчет.
Чтобы создать функцию обработки события, сначала надо выбрать компо-
нент, события которого надо обработать. Компонент можно выбрать щелч-
ком на его изображении в форме или путем выбора в раскрывающемся спи-
ске окна Object Inspector. Затем в окне Object Inspector нужно открыть
вкладку Events.
В левой колонке вкладки Events (рис. 2.19) перечислены события, которые
может воспринимать выбранный компонент. Строго говоря, на вкладке
Events указаны не события, а свойства, значения которых определяют функ-
ции обработки соответствующих событий. Так, например, свойство OnClick
определяет функцию обработки события Click.

Рис. 2.19. Íà вкладке Events перечислены события, которые может воспринимать компонент
(в данном случае — командная кнопка)
Ãëàâà 2. Ïåðâûé ïðîåêò 39

После того как компонент будет выбран, нужно на вкладке Evens выбрать
событие (сделать щелчок мышью на имени события), а затем сделать двойной
щелчок в ставшем доступным поле имени функции обработки события (спра-
ва от имени события). В результате этих действий в модуль формы будет до-
бавлена функция обработки события и станет доступным окно редактора ко-
да (рис. 2.20), в котором можно набирать инструкции функции обработки со-
бытия. Следует обратить внимание, что имя функции обработки события,
формируемое средой разработки, состоит из двух частей. Первая часть иден-
тифицирует форму, вторая — компонент и событие. Так например, имя
функции TForm1::Button1Click показывает, что функция обеспечивает обра-
ботку события Click на компоненте Button1.

Рис. 2.20. Шаблон функции обработки события

Имя функции обработки события можно задать и вручную. Для этого его на-
до просто ввести в поле редактирования, которое находится рядом с именем
соответствующего события.
В листинге 2.1 приведена функция обработки события Click на кнопке Пере-
счет. Обратите внимание на то, как представлена функция. Ее общий вид со-
ответствует тому, как она выглядит в окне редактора кода: ключевые слова
выделены полужирным, комментарии — курсивом (выделение выполняет
редактор кода). Кроме того, инструкции программы набраны с отступами, в
соответствии с принятыми в среде программистов правилами хорошего стиля.
40 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Листинг 2.1. Обработка события Click на кнопке Пересчет

// Щелчок на кнопке Пересчет


void __fastcall TForm1::Button1Click(TObject *Sender)
{
float usd; // цена в долларах
float k; // курс
float rub; // цена в рублях

// ввод исходных данных


k = StrToFloat(Edit1->Text);
usd = StrToFloat(Edit2->Text);

// вычисление
rub = usd * k;

// вывод результата
Label4->Caption = FloatToStrF(usd,ffGeneral,7,2) + "$ = " +
FloatToStrF(rub,ffCurrency,7,2);
}

Функция TForm1::Button1Click пересчитывает цену из долларов в рубли и


выводит результат в поле Label4. Исходные данные (курс и цена в долларах)
вводятся из полей редактирования Edit1 и Edit2 путем обращения к свойст-
ву Text. Значением свойства Text является строка, которая находится в поле
редактирования. Для преобразования строки в число (свойство Text строко-
вого типа) используется функция StrToFloat. Следует обратить внимание,
что функция StrToFloat возвращает результат только в том случае, если
строка, переданная функции в качестве параметра, действительно является
изображением числа в правильном формате (что, при стандартной для России
настройке операционной системы, предполагает использование запятой в ка-
честве разделителя целой и дробной частей числа). Вычисленное значение
цены в рублях выводится в поле Label4 путем присваивания значения свой-
ству Caption. Для преобразования числа в строку (свойство Caption строко-
вого типа) используется функция FloatToStrF.
В листинге 2.2 приведена функция обработки события Click на кнопке За-
вершить (создается она точно так же, как и функция обработки события
Click для командной кнопки Пересчет). В результате щелчка на кнопке За-
вершить программа должна завершить работу. Чтобы это произошло, надо
закрыть окно программы. Делает это метод Close.
Ãëàâà 2. Ïåðâûé ïðîåêò 41

Листинг 2.2. Обработка события Click на кнопке Завершить

// Щелчок на кнопке Завершить


void __fastcall TForm1::Button2Click(TObject *Sender)
{
Form1->Close(); // закрыть форму приложения
}

Ðåäàêòîð êîäà
В процессе набора текста программы редактор кода автоматически выделяет
элементы программы: полужирным — ключевые слова языка программиро-
вания (if, else, while и др.), цветом — константы, курсивом — комментарии.
Это делает текст программы выразительным, облегчает восприятие ее струк-
туры.
В процессе разработки программы часто возникает необходимость переклю-
чения между окном редактора кода и окном формы. Выбрать нужное окно
можно щелчком на ярлыке Code (редактор кода) или Design (редактор фор-
мы) или нажав клавишу <F12>.

Ñèñòåìà ïîäñêàçîê
Во время набора текста программы редактор кода автоматически выводит
список свойств и методов текущего объекта и позволяет выбрать нужное
свойство или метод в списке. Например, если в окне редактора кода набрать
Edit1 (имя компонента, поля редактирования), тире и знак > (больше) (по-
следние два символа в совокупности образуют оператор "ссылка"), то на эк-
ране появится список свойств и методов класса TEdit (рис. 2.21). Програм-
мисту остается только выбрать в списке нужный элемент (свойство или ме-
тод) и нажать клавишу <Enter> (быстро перейти к нужному элементу списка
или к области, где этот элемент находится, можно нажав клавишу, соответст-
вующую первому символу этого элемента).
Следует обратить внимание, что если список свойств и методов не появляет-
ся, то это значит, что в программе обнаружена ошибка. Например, если в ок-
не редактора кода вместо Edit1-> набрать edit1->, то список свойств и мето-
дов не появится, т. к. объект edit1 в программе не определен (здесь предпо-
лагается, что Edit1 — имя компонента), и следовательно, редактор кода не
знает его тип, поэтому-то не выводит список свойств и методов.
После набора имени встроенной или объявленной программистом функции
редактор кода также выводит подсказку: список параметров. Параметр, кото-
рый в данный момент вводит программист, в подсказке выделен полужирным
шрифтом. Например, если набрать слово FloatToStr, которое является име-
42 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 2.21. Редактор кода автоматически выводит список свойств и методов текущего объекта

Рис. 2.22. Пример подсказки


Ãëàâà 2. Ïåðâûé ïðîåêò 43

нем функции преобразования дробного числа в строку символов, и откры-


вающую скобку, то на экране появится окно, в котором будет указан список
параметров функции (рис. 2.22).

Øàáëîíû êîäà
В процессе набора текста программы программист может использовать шаб-
лоны кода (Code Templates). Шаблон кода — это инструкция программы, за-
писанная в общем виде. Например, простейший шаблон инструкции if вы-
глядит так:
if ( )
{

Редактор кода предоставляет программисту большой набор шаблонов: объяв-


ления классов, функций, инструкций выбора (if, switch), циклов (for, while).
Для того чтобы в процессе набора текста программы воспользоваться шабло-
ном кода и вставить его в текст, следует нажать комбинацию клавиш
<Ctrl>+<j> и из появившегося списка выбрать нужный шаблон. Выбрать
шаблон можно обычным образом, прокручивая список. Можно ввести пер-
вую букву имени нужного шаблона, например, i для шаблона if {} else {}.
Выбрав в списке шаблон, надо нажать клавишу <Enter>, шаблон будет встав-
лен в текст программы.
Наиболее часто используемые шаблоны кода редактор вставляет в текст про-
граммы автоматически, как только программист наберет ключевое слово. На-
пример, если в окне редактора кода набрать if и пробел, то в текст програм-
мы будет вставлен шаблон if () {}, причем курсор будет установлен сразу
за открывающей скобкой, чтобы программист сразу мог вводить условие.
После того как условие будет набрано, надо нажать клавишу <Tab>, курсор
переместится за открывающую фигурную скобку.

Ñïðàâî÷íàÿ èíôîðìàöèÿ
В процессе набора программы можно получить справку о конструкции языка,
типе данных, процедуре или функции. Чтобы получить доступ к нужному
разделу справочной информации, необходимо направить справочной системе
соответствующий запрос. Для этого нужно в окне редактора кода установить
курсор на ключевое слово языка программирования, идентификатор типа или
имя функции и нажать клавишу <F1>. Например, чтобы получить справку о
функции FloatToStrF, надо в окне редактора кода установить курсор на лю-
бой символ ее имени и нажать клавишу <F1>.
44 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

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


меню Help выбрать команду CodeGear Help, в появившемся окне справоч-
ной информации раскрыть вкладку Index и в поле Look for ввести ключевое
слово. В результате будет сформирован список разделов, связанных с вве-
денным словом.

Ñîõðàíåíèå ïðîåêòà
Проект — это набор файлов, используя которые компилятор создает про-
грамму — exe-файл. В простейшем случае, когда программа представляет
собой однооконное приложение, проект образуют:
 файл описания проекта (cbproj-файл);
 файл главного модуля (cpp-файл);
 файл описания формы (dfm-файл);
 файлы модуля формы (h- и cpp-файлы);
 файл ресурсов (res-файл).
Чтобы сохранить проект, нужно в меню File выбрать команду Save Project.
Сначала на экране появляется окно Save Unit. В этом окне (рис. 2.23) надо

Рис. 2.23. Сохранение модуля


Ãëàâà 2. Ïåðâûé ïðîåêò 45

открыть папку, предназначенную для проектов (по умолчанию в Windows XP


проекты C++ Builder сохраняются в папке C:\Document and Settings\User\Мои
документы\RAD Studio\Projects), создать новую папку для сохраняемого про-
екта, открыть ее, в поле Имя файла ввести имя модуля формы и сделать
щелчок на кнопке Сохранить.
На экране появится окно Save Project (рис. 2.24), в поле Имя файла которого
надо ввести имя проекта. Следует обратить внимание на то, что по умолча-
нию имя проекта определяет имя exe-файла, генерируемого компилятором.
Поэтому проекту следует присвоить такое имя, которое должно быть у exe-
файла.

Рис. 2.24. Сохранение проекта

Ñòðóêòóðà ïðîåêòà
Проект представляет собой совокупность файлов, которые используются
компилятором для генерации выполняемого файла. Основу проекта обра-
зуют:
 файл проекта (cbproj-файл);
 файл главного модуля (cpp-файл);
 файл ресурсов (res-файл);
46 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

 один или несколько модулей формы (для каждой формы C++ Builder соз-
дает три файла: dfm-, h- и cpp-файл).
Главный модуль (чтобы его увидеть, надо в меню Project выбрать команду
View Source) содержит инструкции, обеспечивающие инициализацию при-
ложения, создание стартовой формы и запуск программы.
В качестве примера в листинге 2.3 приведен главный модуль приложения
Конвертор.

Листинг 2.3. Главный модуль приложения Конвертор (usd2rub.cpp)

#include <vcl.h>
#pragma hdrstop
//--------------------------------------------------------------------
USEFORM("Unit1.cpp", Form1);
//--------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
try
{
Application->Initialize();
Application->CreateForm(__classid(TForm1), &Form1);
Application->Run();
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
catch (...)
{
try
{
throw Exception("");
}
catch (Exception &exception)
{
Application->ShowException(&exception);
}
}
return 0;
}

Начинается главный модуль директивой компилятору (точнее, препроцессо-


ру). Директива #include <vcl.h> информирует компилятор, что перед тем
Ãëàâà 2. Ïåðâûé ïðîåêò 47

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


заголовочный файл библиотеки визуальных компонентов — vcl.h.
Строка USEFORM("unit1.cpp", Form1) указывает, что в проект нужно вклю-
чить модуль формы (файл unit1.cpp), который содержит функции обработки
событий для компонентов формы Form1.
Далее следует описание главной функции программы WinMain. Эта функция
инициализирует внутренние структуры программы, создает форму Form1 и
запускает программу (в результате на экране появляется окно программы).
Так как в проекте Конвертор только одна форма, то на экране именно она и
появляется.
Затем следуют инструкции обработки исключений (ошибок), которые могут
возникнуть как во время запуска программы, так и во время ее работы. Таким
образом, главный модуль обеспечивает вывод на экран стартовой формы
программы. Дальнейшее поведение программы определяют функции обра-
ботки событий стартовой формы.
Помимо главного модуля, в состав проекта входят модули формы. Как было
сказано ранее, для каждой формы C++ Builder создает отдельный модуль
(dfm-, h- и cpp-файл). Заголовочный файл содержит сформированное
C++ Builder описание класса формы (листинг 2.4), файл кода — функции об-
работки событий (листинг 2.5). Файл описания формы (dfm-файл) использу-
ется дизайнером формы для формирования на вкладке Design изображения
формы.

Листинг 2.4. Заголовочный файл модуля формы (unit1.h)

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TEdit *Edit1;
TEdit *Edit2;
TLabel *Label1;
TLabel *Label2;
TLabel *Label3;
48 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

TLabel *Label4;
TButton *Button1;
TButton *Button2;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------
#endif

Листинг 2.5. Модуль формы (unit1.cpp)

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}

// Щелчок на кнопке Пересчет


void __fastcall TForm1::Button1Click(TObject *Sender)
{
float k; // курс
float usd; // цена в долларах

float rub; // цена в рублях

// ввод исходных данных


k = StrToFloat(Edit1->Text);
usd = StrToFloat(Edit2->Text);
Ãëàâà 2. Ïåðâûé ïðîåêò 49

// вычисление
rub = usd * k;

// вывод результата
Label4->Caption = FloatToStrF(usd,ffGeneral,7,2) + "$ = " +
FloatToStrF(rub,ffCurrency,7,2);
}

// Щелчок на кнопке Завершить


void __fastcall TForm1::Button2Click(TObject *Sender)
{
Form1->Close(); // закрыть форму приложения
}

Следует отметить, что значительное количество кода сгенерировано средой


разработки на основе анализа действий программиста. Так C++ Builder пол-
ностью сформировал главный модуль (usd2rub.cpp), заголовочный файл мо-
дуля формы (Unit1.h), значительную часть модуля формы (Unit1.cpp). Также
C++ Builder сформировал описание формы, файл проекта и файл ресурсов.

Êîìïèëÿöèÿ
Процесс преобразования исходной программы в выполняемую называется
компиляцией и состоит из двух этапов: трансляции и компоновки. На этапе
трансляции выполняется перевод (translate — переводить) модулей исходной
программы в промежуточное представление (так называемый объектный
код). На этапе компоновки выполняется объединение (link — связывать) от-
дельных модулей (obj-файлов), полученных на этапе трансляции, и библио-
тек в единую программу (exe-файл).
Чтобы выполнить компиляцию, надо в меню Project выбрать команду Make
Project или Build Project, где Project — имя компилируемого проекта.
Различие команд Make и Build состоит в следующем: команда Make выпол-
няет компиляцию только тех модулей, в которые после последней компиля-
ции были внесены изменения, а команда Build — всех модулей проекта, не-
зависимо от того, есть ли в них изменения или нет. Компиляция, активизиро-
ванная командой Make, как правило, выполняется быстрее. Компиляция
также активизируется и в результате запуска программы (команда Run или
Run Without Debugging в меню Run), если с момента предыдущей компиля-
ции в программу были внесены изменения.
Если программа состоит из более чем двух модулей (простейшая программа
состоит из двух модулей: главного модуля и модуля формы), то выполнить
50 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

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


брав в меню Project команду Build Unit (Unit — имя файла модуля).
Процесс и результат компиляции отражается в окне Compiling. Если в про-
грамме нет синтаксических ошибок, то компилятор в подкаталоге Debug ка-
талога проекта создает exe-файл и выводит сообщение об успешном завер-
шении компиляции (рис. 2.25). Если в программе есть ошибки, то в окно
Compiling выводится сообщение Done: There are errors (рис. 2.26).

Рис. 2.25. Результат компиляции: компиляция выполнена успешно

Рис. 2.26. Результат компиляции: в программе есть ошибки

В случае обнаружения ошибок в окне Compiling отображается информация о


количестве синтаксических (Error) и семантических (Warning) ошибок, сами
же сообщения отображаются в окне Messages (рис. 2.27).
Если в программе есть ошибки, то по завершении процесса компиляции в
окне редактора кода автоматически выделяется строка, в которой находится
первая из обнаруженных ошибок. Чтобы перейти к фрагменту кода, который
содержит другую ошибку, надо в окне Message сделать двойной щелчок ле-
вой кнопкой мыши в строке сообщения об ошибке.
Ãëàâà 2. Ïåðâûé ïðîåêò 51

Рис. 2.27. Сообщения об ошибках

Îøèáêè
Как было сказано ранее, компилятор создает exe-файл только в том случае,
если в программе нет синтаксических (Error) ошибок. Если в программе есть
ошибки, то их надо устранить. Процесс исправления ошибок носит итераци-
онный характер. Обычно сначала устраняют наиболее очевидные ошибки,
например, объявляются не объявленные переменные. После очередного вне-
сения изменений в текст программы выполняется повторная компиляция.
Следует обратить внимание на то, что компилятор не всегда может точно ло-
кализовать ошибку. Поэтому, анализируя фрагмент программы, который вы-
делен компилятором как ошибочный, нужно обращать внимание не только на
него, но и на те инструкции, которые находятся в предыдущих строках.
Например, в следующем фрагменте кода:
// вычисление
rub = usd * k

// вывод результата
Label4->Caption = FloatToStrF(rub, ffCurrency,7,2);

не поставлена точка с запятой после инструкции rub = usd * k.


Компилятор обнаруживает это, выводит сообщение Statement missing ;, вы-
деляет строку Label4->Caption = FloatToStrF(rub, ffCurrency,7,2) и ус-
танавливает курсор после идентификатора Label4 (т. е. указывает, что точку
с запятой надо поставить после Label4).
В табл. 2.10 перечислены типичные ошибки и соответствующие им сообще-
ния компилятора.
Если компилятор обнаружил достаточно много ошибок, то, просмотрев все
сообщения и устранив сначала наиболее очевидные ошибки, выполните по-
вторную компиляцию. Вполне вероятно, что после этого количество ошибок
52 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

значительно уменьшится. Это объясняется особенностями синтаксиса языка


С++, когда одна незначительная ошибка может "тащить" за собой довольно
большое количество других.

Òàáëèöà 2.10. Òèïè÷íûå îøèáêè

Сообщение Ошибка
Undefined symbol Используется не объявленная переменная
(неизвестный символ) Имя переменной, функции или параметра
записано неверно. Например, в программе
объявлена переменная Sum, а в инструкциях
используется sum
Statement missing; После инструкции не поставлена точка
(отсутствует точка с запятой) с запятой
Unterminated string or В конце строковой константы, например
character constant текста сообщения, нет двойных кавычек
(незаконченная строковая или
символьная константа)
) expected При записи арифметического выражения,
(ожидается закрывающая скобка) содержащего скобки, нарушен баланс
открывающих и закрывающих скобок
if statement missing ( В инструкции if условие не заключено
(в инструкции if нет открывающей в скобки
скобки)
Compound statement missing } Нарушен баланс открывающих и закрываю-
щих фигурных скобок. Вероятно не постав-
лена закрывающая фигурная скобка, отме-
чающая конец функции или группы инструк-
ций, например, после условия или слова
else в инструкции if
Extra parameter in call to Неверно записана инструкция вызова функ-
(лишний параметр при вызове ции, указан лишний параметр
функции)

Ïðåäóïðåæäåíèÿ è ïîäñêàçêè
При обнаружении в программе неточностей, которые не являются ошибками,
компилятор выводит предупреждения (Warnings) и подсказки (Hints).
Например, наиболее часто выводимой подсказкой является сообщение об
объявленной, но не используемой переменной:
... is declared but never used.

Действительно, зачем объявлять переменную и не использовать ее?


Ãëàâà 2. Ïåðâûé ïðîåêò 53

В табл. 2.11 приведены предупреждения, наиболее часто выводимые компи-


лятором.

Òàáëèöà 2.11. Ïðåäóïðåæäåíèÿ êîìïèëÿòîðà

Предупреждение Вероятная причина

Possibly incorrect assignment. В условии (например, в инструк-


(вероятно инструкция присваивания некор- ции if) вместо оператора срав-
ректная) нения (= =) использован оператор
присваивания (=)
Possibly use of ... before definition. Не присвоено начальное
(вероятно используется не инициализирован- значение переменной
ная переменная)
... is declared but never used Невнимательность программиста
(идентификатор объявлен, но не используется )
... is assigned a value that is never Невнимательность программиста
used
(объект получил значение, но не используется)
Parameter ... is never used
(параметр не используется)

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


Например, в функцию обработки событий передается параметр Sender, кото-
рый внутри функции используется не всегда. В результате компилятор выво-
дит сообщение:
Parameter 'Sender' is never used,
причем столько раз, сколько функций обработки событий содержит компи-
лируемый модуль. Чтобы отключить отображение этого сообщения, надо
в меню Project выбрать команду Options, раскрыть вкладку C++ Compiler —
Warnings и сбросить флажок, соответствующий сообщению W8057
(рис. 2.28).

Êîìïîíîâêà
Процесс создания exe-файла состоит из двух этапов: трансляции и компонов-
ки. Если в программе нет синтаксических ошибок, то процесс компоновки
запускается автоматически по завершении процесса компиляции, о чем сви-
детельствует сообщение Linking... (рис. 2.29) в окне Compiling (процесс
компоновки простой программы протекает довольно быстро, поэтому сооб-
щение Linking... можно и не заметить).
54 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 2.28. Настройка системы предупреждений

Рис. 2.29. Сообщение о компоновке программы

На этапе компоновки также могут возникнуть ошибки. В основном причина


ошибок во время компоновки состоит в недоступности файлов библиотек или
других ранее откомпилированных модулей. Устраняются эти ошибки путем
настройки среды разработки и включением в проект недостающих модулей.
Ãëàâà 2. Ïåðâûé ïðîåêò 55

В простых проектах ошибки во время компоновки, как правило, не возни-


кают.

Çàïóñê ïðîãðàììû
Чтобы запустить программу, надо сделать щелчок на кнопке Run (рис. 2.30),
находящейся на панели инструментов. Можно в меню Run выбрать команду
Run (или Run Without Debugging). Можно также нажать клавишу <F9>.

Рис. 2.30. Чтобы запустить программу, сделайте щелчок на кнопке Run

Команда Run запускает программу в режиме отладки, команда Run Without


Debugging — в обычном режиме, даже в том случае, если в ней есть инфор-
мация, необходимая для отладки (заданы точки останова, указаны перемен-
ные, значения которых надо контролировать). Следует обратить внимание,
что процесс запуска программы командой Run Without Debugging происхо-
дит быстрее.

Èñêëþ÷åíèÿ
Во время работы программы могут возникать исключения (exceptions) —
ошибки. В большинстве случаев причинами исключений являются неверные
данные.
Например, если во время работы программы Конвертор в поле Курс ввести,
например, 24.5 , то в результате щелчка на кнопке Вычислить на экране
появится сообщение об ошибке (рис. 2.31).

Рис. 2.31. Пример сообщения об ошибке (программа запущена


командой Run Without Debugging)

Причина возникновения ошибки в следующем. Преобразование строки, вве-


денной в поле редактирования, в число выполняет функция StrToFloat. Эта
функция работает правильно, если ее параметром является строковое пред-
56 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

ставление дробного числа, что предполагает использование запятой в качест-


ве десятичного разделителя (при стандартной настройке Windows). В рас-
сматриваемом случае строка 24.5 не является строковым представлением
дробного числа (вместо запятой стоит точка), поэтому и возникает исклю-
чение.
Если программа запущена из среды разработки в режиме отладки (команда
Run Run), то при возникновении исключения выполнение программы
приостанавливается, и на экране появляется окно Debugger Exception
Notification, в котором, помимо сообщения об ошибке, указывается класс ис-
ключения (рис. 2.32). Чтобы завершить выполнение программы, надо сделать
щелчок на кнопке Break и затем в меню Run выбрать команду Program
Reset. Щелчок на кнопке Continue запускает программу с той точки, в кото-
рой была приостановлена ее работа.

Рис. 2.32. Сообщение о возникновении исключения


(программа запущена из C++ Builder)

Îáðàáîòêà èñêëþ÷åíèé
Обработку исключений берет на себя автоматически добавляемый в выпол-
няемую программу код, который обеспечивает в том числе и вывод сообще-
ния об ошибке. Вместе с тем программист может поместить в программу код,
который выполнит обработку исключения.
Инструкция обработки исключения в общем виде выглядит так:
try
{
// здесь инструкции, выполнение которых может вызвать исключение
}
catch ( Тип &e)
{
// здесь инструкции обработки исключения
}
Ãëàâà 2. Ïåðâûé ïðîåêò 57

Слово try отмечает начало секции, в которой находятся инструкции, при вы-
полнении которых возможно возникновение исключений, и указывает, что
обработку этих исключений берет на себя программа.
Слово catch отмечает начало секции обработки исключения. Инструкции
этой секции будут выполнены, если в программе возникнет исключение ука-
занного типа. Следует обратить внимание, что инструкции, следующие за
той, при выполнении которой возникло исключение, после обработки исклю-
чения не выполняются.
В табл. 2.12 приведены наиболее часто возникающие исключения и указаны
причины, которые могут привести к их возникновению.

Òàáëèöà 2.12. Òèïè÷íûå èñêëþ÷åíèÿ

Исключение Возникает

EConvertError — При выполнении преобразования, если преобразуемая


ошибка преобразова- величина не может быть приведена к требуемому типу.
ния Наиболее часто возникает при преобразовании строки
символов в число
EDivByZero — При выполнении операции целочисленного деления, если
целочисленное деле- делитель равен нулю
ние на ноль
EZeroDivide — При выполнении операции деления над дробными опе-
деление на ноль рандами, если делитель равен нулю
EFOpenError При обращении к файлу, например, при попытке загрузить
файл иллюстрации с помощью метода LoadFromFile.
Наиболее частой причиной является отсутствие требуе-
мого файла или, в случае использования сменного диска,
отсутствие диска в накопителе
EInOutError — При выполнении файловых операций. Наиболее частой
ошибка ввода/вывода причиной является отсутствие требуемого файла или,
в случае использования сменного диска, отсутствие диска
в накопителе
EOleException При выполнении операций с базой данных, например, при
попытке открыть несуществующую базу данных, если для
доступа к базе данных используются ADO-компоненты
(чтобы иметь возможность обработки этого исключения,
в директиву uses надо добавить ссылку на модуль
ComObj)

В качестве примера в листинге 2.6 приведена функция обработки события


Click на кнопке Пересчет (программа Конвертор), в которую добавлены
инструкции обработки исключения EConvertError. При возникновении ис-
58 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

ключения программа определяет причину (незаполненное поле или неверный


формат данных) и выводит соответствующее сообщение (рис. 2.33).

Листинг 2.6. Щелчок на кнопке Пересчет (пример обработки исключения)

// Щелчок на кнопке Пересчет


void __fastcall TForm1::Button1Click(TObject *Sender)
{
float usd; // цена в долларах
float k; // курс
float rub; // цена в рублях

try
{
// ввод исходных данных
usd = StrToFloat(Edit1->Text);
k = StrToFloat(Edit2->Text);
}

catch (EConvertError &e)


{
if ( (Edit1->Text.Length() == 0 )||(Edit2->Text.Length() == 0 ))
MessageDlg("Надо ввести данные в оба поля!",
mtWarning, TMsgDlgButtons() << mbOK,0);
else
MessageDlg("При вводе дробных чисел используйте запятую!",
mtWarning, TMsgDlgButtons() << mbOK,0);
return;
}
// вычисление
rub = usd * k;

// вывод результата
Label4->Caption = FloatToStrF(usd,ffGeneral,7,2) + "$ = " +
FloatToStrF(rub, ffCurrency,7,2);
}

Рис. 2.33. Сообщение об ошибке


Ãëàâà 2. Ïåðâûé ïðîåêò 59

В приведенном примере для информирования пользователя о возникнове-


нии ошибки использована функция MessageDlg, инструкция вызова которой
в общем виде выглядит так:
r = MessageDlg(Сообщение, Тип, Кнопки, РазделСправки)

где:
 Сообщение — текст сообщения;
 Тип — тип сообщения. Сообщение может быть информационным
(mtInformation), предупреждающим (mtWarning) или сообщением об
ошибке (mtError). Каждому типу сообщения соответствует значок
(табл. 2.13);
 Кнопки — список кнопок, отображаемых в окне сообщения (табл. 2.14);
 РазделСправки — идентификатор раздела справочной информации, кото-
рый появится на экране, если пользователь нажмет клавишу <F1>. Если
вывод справки не предусмотрен, то в качестве параметра следует указать
ноль.
Значение, возвращаемое функцией MessageDlg (табл. 2.15), позволяет опреде-
лить, какая кнопка была нажата пользователем для завершения диалога.

Òàáëèöà 2.13. Ñîîáùåíèÿ

Сообщение Тип сообщения Значок

Внимание mtWarning

Ошибка mtError

Информация mtInformation

Подтверждение mtConfirmation

Òàáëèöà 2.14. Èäåíòèôèêàòîðû êíîïîê

Идентификатор Кнопка

mbYes Yes
mbNo No
mbOK OK
mbCancel Cancel
mbHelp Help
60 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 2.15. Çíà÷åíèÿ ôóíêöèè MessageDlg

Значение Диалог завершен нажатием кнопки


mrYes Yes
mrOk Ok
mrNo No
mrCancel Cancel

Для вывода сообщений можно использовать функцию ShowMessage, которая


выводит окно с сообщением и кнопкой OK (рис. 2.34).

Рис. 2.34. Сообщение, выведенное процедурой ShowMessage

Инструкция вызова функции ShowMessage в общем виде выглядит так:


ShowMessage(Сообщение);

где Сообщение — это выражение строкового типа, т. е. текст, который надо


отобразить в окне сообщения.
Обратите внимание: в заголовке окна сообщения, выводимого функцией
ShowMessage, отображается имя приложения или (если оно не задано) имя
проекта (выполняемого файла).

Âíåñåíèå èçìåíåíèé
Программу Конвертор можно существенно улучшить. Например, сделать
так, чтобы в поля редактирования можно было вводить только правильные
данные, чтобы в результате нажатия клавиши <Enter> (после ввода данных в
поле Курс) курсор автоматически переходил в поле Цена, а после ввода цены
становилась активной кнопка Пересчет. Кроме того, можно сделать так, что-
бы кнопка Пересчет становилась доступной только в том случае, если дан-
ные введены в оба поля.
Чтобы внести изменения в программу, нужно открыть соответствующий про-
ект. Сделать это можно обычным способом, выбрав в меню File команду
Open Project. Если надо открыть проект, над которым программист работал
Ãëàâà 2. Ïåðâûé ïðîåêò 61

недавно, то его можно выбрать на странице Welcome (в списке Recent


Projects) или в списке, который отображается в результате выбора в меню
File команды Reopen.
Чтобы программа Конвертор работала так, как было описано выше, надо
создать функции обработки событий KeyPress и Change для полей редакти-
рования (компонентов) Edit1 и Edit2.
Модуль формы усовершенствованной программы Конвертор приведен в
листинге 2.7.
Функция обработки события KeyPress (для каждого компонента своя) обес-
печивает фильтрацию символов, вводимых в поле редактирования. Она про-
веряет код нажатой клавиши (значение параметра Key). Если символ невер-
ный (правильными символами в данном случае считаются цифры, запятая,
а также символы, соответствующие клавишам <Enter> и <Backspace>), то
функция заменяет его на так называемый нуль-символ. В результате замены
символ в поле редактирования не появляется и у пользователя создается впе-
чатление, что клавиатура не воспринимает нажатие клавиши. Следует обра-
тить внимание, что теперь в поля редактирования не могут попасть неверные
данные, а следовательно, исключение EСonvertError возникнуть не может.
Поэтому в функции обработки события KeyPress на кнопке Пересчет нет
инструкций, обеспечивающих обработку исключения EConvertError.
Функция обработки события Change управляет доступностью кнопки Пере-
счет (чтобы в начале работы программы кнопка Пересчет была недоступна,
свойству Enabled надо присвоить значение false). Событие Change возника-
ет, если текст, находящийся в поле редактирования, изменился (т. е. происхо-
дит при каждом нажатии клавиши, в результате которого в поле редактиро-
вания появляется новый символ или стирается набранный). Функция обра-
ботки этого события проверяет, есть ли данные в полях редактирования, и
если какое-либо из полей пустое, то присваивает свойству Enabled кнопки
Button1 значение false, тем самым делая кнопку недоступной.

Следует обратить внимание: функция EditChange обрабатывает событие


Change обоих компонентов Edit. Процесс создания функции, обеспечиваю-
щей обработку событий от разных компонентов, следующий. Сначала надо
создать функцию обработки события Change для одного из компонентов (до-
пустим, Edit1), назвав ее, например, EditChange (делается это редактирова-
нием имени функции на вкладке Events окна Object Inspector). После этого
следует выбрать компонент Edit2, на вкладке Events выбрать событие
Change, раскрыть список функций обработки событий и выбрать функцию,
обеспечивающую обработку события Change компонента Edit1 (рис. 2.35).
62 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 2.35. Выбор функции обработки события

Листинг 2.7. Модуль формы (Unit1.cpp)

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//-----------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

//----------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}

// Щелчок на кнопке Пересчет


void __fastcall TForm1::Button1Click(TObject *Sender)
{
float usd; // цена в долларах
Ãëàâà 2. Ïåðâûé ïðîåêò 63

float k; // курс
float rub; // цена в рублях

// ввод исходных данных


usd = StrToFloat(Edit1->Text);
k = StrToFloat(Edit2->Text);

// вычисление
rub = usd * k;

// вывод результата
Label4->Caption = FloatToStrF(usd,ffGeneral,7,2) + "$ = " +
FloatToStrF(rub, ffCurrency,7,2);
}

// Щелчок на кнопке Завершить


void __fastcall TForm1::Button2Click(TObject *Sender)
{
Form1->Close(); // закрыть форму приложения
}

// изменилось содержимое поля редактирования


void __fastcall TForm1::EditChange(TObject *Sender)
{
if ((Edit1->Text.Length() == 0 ) || (Edit2->Text.Length() == 0))
{
Button1->Enabled = false;
}
else
Button1->Enabled = true;
}

// нажатие клавиши в поле Курс


void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
// код запрещенного символа заменим нулем, в результате
// символ в поле редактирования не появится

// Key — код нажатой клавиши (символа)


// проверим, является ли символ допустимым
if ((Key >= '0') && (Key <= '9')) // цифра
return;
64 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

// глобальная переменная DecimalSeparator


// содержит символ, используемый в качестве разделителя
// при записи дробных чисел
if (Key == DecimalSeparator)
{
if ((Edit1->Text).Pos(DecimalSeparator) != 0)
Key = 0; // разделитель уже введен
return;
}

if (Key == VK_BACK) // клавиша <Backspace>


return;

if (Key == VK_RETURN) // клавиша <Enter>


{
Edit2->SetFocus(); // переместить курсор в поле Цена
return;
}

// остальные символы запрещены


Key = 0; // не отображать символ
}

// нажатие клавиши в поле Цена


void __fastcall TForm1::Edit2KeyPress(TObject *Sender, char &Key)
{
if ((Key >= '0') && (Key <= '9')) //цифра
return;

if (Key == DecimalSeparator)
{
if ((Edit2->Text).Pos(DecimalSeparator) != 0)
Key = 0; // разделитель уже введен
return;
}

if (Key == VK_BACK) // клавиша <Backspace>


return;

if (Key == VK_RETURN) // клавиша <Enter>


{
Button1->SetFocus();// сделать активной кнопку Вычислить
return;
};
Ãëàâà 2. Ïåðâûé ïðîåêò 65

// остальные клавиши запрещены


Key = 0; // не отображать символ
}

Хотя и говорят, что "лучшее — враг хорошего", но, тем не менее, программу
Конвертор можно еще улучшить.
Функции обработки события KeyPress для компонентов Edit1 и Edit2
практически ничем не отличаются (см. лиcтинг 2.7). Различие только в том,
как обрабатывается нажатие клавиши <Enter>. Возникает вопрос, а можно ли
сделать так, чтобы одна функция обрабатывала события разных компонентов
по-разному? Оказывается, можно. Обратите внимание: у функции обработки
события KeyPress есть параметр Sender — ссылка на объект (компонент), на
котором событие произошло. Таким образом функция может определить, на
каком компоненте произошло событие, и выполнить соответствующее дейст-
вие. Функция EditKeyPress, обеспечивающая обработку событий KeyPress
компонентов Edit1 и Edit2, приведена в листинге 2.8 (процесс создания
функции, обеспечивающей обработку событий от разных компонентов, уже
был подробно описан на примере создания функции обработки события
Change).

Листинг 2.8. Обработка одной функцией событий от разных компонентов

// нажатие клавиши в поле Цена или Курс


void __fastcall TForm1::EditKeyPress(TObject *Sender, char &Key)
{
// проверим, является ли символ допустимым

if ((Key >= '0') && (Key <= '9')) // цифра


return;

// Обработку десятичного разделителя


// сделаем интеллектуальной
if ((Key == '.')||(Key == ','))
{
// Независимо от того, какой символ — точку
// или запятую нажал пользователь, будем считать
// что это — десятичный разделитель
Key = DecimalSeparator;

if ( (((TEdit*)Sender)->Text).Pos(DecimalSeparator) != 0)
Key = 0; // разделитель уже введен
return;
}
66 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

if (Key == VK_BACK) // клавиша <Backspace>


return;

if (Key == VK_RETURN) // клавиша <Enter>


{
if ( ((TEdit*)Sender)->Name == "Edit1" )
Edit2->SetFocus(); // переместить курсор в поле Цена
else
Button1->SetFocus(); // сделать активной кнопку Пересчет
}

Key = 0; // остальные клавиши запрещены


}

Íàñòðîéêà ïðèëîæåíèÿ
После того как приложение будет отлажено, можно выполнить его оконча-
тельную настройку — задать название программы.
Значок и название программы отображаются в панели задач во время работы
программы. Значок также изображает программу (exe-файл) в папке, на рабо-
чем столе и в меню команд. По умолчанию в качестве значка приложения
используется стандартный значок, а в качестве названия — имя проекта (exe-
файла).
Чтобы задать название и значок программы, надо в меню Project выбрать
команду Options и раскрыть вкладку Application(рис. 2.36). Название про-
граммы надо ввести в поле Title. Чтобы задать значок, следует сделать щел-
чок на кнопке Load Icon. В результате откроется окно просмотра папок, в
котором надо открыть папку, содержащую значки, и указать подходящий
значок.
Программист может создать уникальный значок для своей программы. Сде-
лать это можно, например, при помощи утилиты Borland Image Editor.
Чтобы создать значок (ico-файл), надо запустить Image Editor, в меню File
выбрать команду NewIcon File и в появившемся окне Icon Properties за-
дать характеристики значка (рис. 2.37). В результате описанных выше дейст-
вий станет доступным окно графического редактора (рис. 2.38).
Процесс рисования в Image Editor обычный. Следует обратить внимание на
палитру. Помимо обычных 16 красок на ней есть "прозрачный" цвет (перво-
начально поле рисунка окрашено этим цветом). Точки, окрашенные этим
цветом, при отображении значка (например, на рабочем столе) принимают
цвет точек поверхности, на которую будет выведен рисунок.
Ãëàâà 2. Ïåðâûé ïðîåêò 67

Следует обратить внимание, что в одном ico-файле может находиться не-


сколько разных значков (разными считаются значки с разными характеристи-
ками). Значки 32×32 используются при отображении программы на рабочем
столе и в папке (в режиме Значки), значок 16×16 — для отображения про-
граммы в папке (в режиме Список) и в меню команд. Поэтому, завершив ра-
боту над значком 32×32, сделайте щелчок на кнопке New, выберите 16×16 и
нарисуйте картинку для значка этого размера.

Рис. 2.36. Вкладка Application

Рис. 2.37. Выбор характеристик значка


68 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 2.38. Создать значок можно в Image Editor

После того как картинки для значков будут готовы, надо в меню File выбрать
команду Save и в появившемся окне задать имя ico-файла.

Çàâåðøåíèå ïðîåêòà
По умолчанию компилятор создает выполняемый файл в подкаталоге Debug
каталога проекта. Открыв подкаталог Debug каталога проекта, можно уви-
деть, что в нем, помимо exe-файла, есть и другие файлы: с расширением obj,
map, tds, pch и др. Все эти файлы создаются в процессе компиляции и компо-
новки модулей, в них находится промежуточная служебная информация, не-
обходимая для формирования exe-файла. По окончании работы над проектом
obj-, map-, tds- и pch-файлы можно удалить, тем более что их размер достига-
ет нескольких мегабайт.

Óñòàíîâêà ïðèëîæåíèÿ
íà äðóãîé êîìïüþòåð
Программу, созданную в C++ Builder, можно перенести на другой компью-
тер, например, записав ее на USB Flash-устройство.
Ãëàâà 2. Ïåðâûé ïðîåêò 69

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


библиотек (Dynamic RTL) и пакетов компонентов (Runtime packages). Поэто-
му приложение, созданное в C++ Builder, будет работать на другом компью-
тере только в том случае, если на нем есть динамические библиотеки и паке-
ты компонентов, необходимые для работы приложения (на компьютер
программиста эти библиотеки устанавливаются в процессе установки
C++ Builder). Таким образом, помимо exe-файла, на компьютер пользователя
надо перенести как минимум: библиотеку Runtime Library (файл rtl100.bpl),
библиотеку визуальных компонентов (Visual Components Library, файл
vcl100.bpl), а также библиотеки borndmm.dll и cc3280mt.dll. Эти библиотеки
можно поместить в тот же каталог, что и exe-файл или (если библиотеки ис-
пользуется несколькими программами) в каталог C:\Windows\System32.
Необходимо обратить внимание на то, что библиотечные функции, исполь-
зуемые программой, можно включить в exe-файл (в этом случае библиотеки
rtl100.bpl, vcl100.bpl и др., кроме borndmm.dll и cc3280mt.dll, на компьютер
пользователя переносить не надо). Чтобы это сделать, нужно задать режим
компиляции без использования динамических библиотек и пакетов компо-
нентов: выбрать в меню Project команду Options и на вкладках Linking и
Packages сбросить, соответственно, флажки Dynamic RTL и Build with
runtime packages. После этого надо выполнить компиляцию проекта (в меню
Project выбрать команду Build).
Профессиональный подход к разработке программного обеспечения предпо-
лагает, что установка программы на компьютер пользователя должна выпол-
няться с минимальным его участием. Поэтому программист должен разрабо-
тать не только приложение, но и программу, обеспечивающую его установку
на компьютер пользователя. Сделать программу установки можно точно так
же, как и любое другое приложение. Однако лучше, да и проще, воспользо-
ваться, например, утилитой InstallAvare, которая позволяет сформировать
образ установочного диска, не написав ни единой строчки кода. Процесс соз-
дания образа установочного диска в InstallAware описан в главе 9.
ÃËÀÂÀ 3

Áàçîâûå êîìïîíåíòû

В этой главе на конкретных примерах демонстрируется назначение основных


базовых компонентов.
К базовым можно отнести компоненты, обеспечивающие взаимодействие
с пользователем (Edit, Label, Button и др.), отображение иллюстраций
(Image), стандартных диалоговых окон (SaveDialog, OpenDialog) и некоторые
другие, например, Timer. Базовые компоненты находятся на вкладках
Standard, Additional, Win32, System и Dialogs.

Label
Компонент Label предназначен для отображения текста. Его значок находит-
ся на вкладке Standard (рис. 3.1). Задать текст, отображаемый в поле компо-
нента, присвоив значение свойству Caption, можно как во время разработки
формы, так и во время работы программы. Свойства компонента Label при-
ведены в табл. 3.1.

Òàáëèöà 3.1. Ñâîéñòâà êîìïîíåíòà Label

Свойство Описание

Name Имя (идентификатор) компонента

Caption Отображаемый текст

Left Расстояние от левой границы поля вывода до левой границы


формы
Top Расстояние от верхней границы поля вывода до верхней границы
формы
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 71

Òàáëèöà 3.1 (îêîí÷àíèå)

Свойство Описание
Height Высота поля вывода
Width Ширина поля вывода
AutoSize Признак того, что размер поля определяется его содержимым
WordWrap Признак того, что слова, которые не помещаются в текущей стро-
ке, автоматически переносятся на следующую строку (значение
свойства AutoSize должно быть false)
Alignment Задает способ выравнивания текста внутри поля. Текст может
быть выровнен по левому краю (taLeftJustify), по центру
(taCenter) или по правому краю (taRightJustify)
Font Шрифт, используемый для отображения текста. Уточняющие
свойства определяют шрифт (Name), размер (Size), стиль (Style)
и цвет символов (Color)
ParentFont Признак наследования компонентом характеристик шрифта фор-
мы, на которой находится компонент. Если значение свойства
равно true, то текст выводится шрифтом, установленным для
формы
Color Цвет фона области вывода текста
Transparent Управляет отображением фона области вывода текста. Значение
true делает область вывода текста прозрачной (область вывода
не закрашивается цветом, заданным свойством Color)
Visible Позволяет скрыть текст (false) или сделать его видимым (true)

Рис. 3.1. Значок компонента Label

Следующая программа (ее форма приведена на рис. 3.2, а текст функции об-
работки события Click на кнопке Пересчет — в листинге 3.1) демонстриру-
ет возможности компонента Label. Она показывает, как вывести в поле ком-
понента значение переменной, а также как изменить цвет текста, отображае-
мого в поле компонента.

Программа пересчитывает вес из фунтов в килограммы. Если пользователь не


введет данные, то в результате щелчка на кнопке Пересчет в поле компонен-
72 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

та Label2 красным цветом отображается сообщение об ошибке. Значения


свойств компонентов Label приведены в табл. 3.2.

Рис. 3.2. Форма программы Фунты-килограммы

Òàáëèöà 3.2. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ Label

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


Label1 Caption Фунты
Label2 Caption
AutoSize false
WordWrap true
Width 289
Height 25

Листинг 3.1. Обработка события Click на кнопке Пересчет

// щелчок на кнопке Пересчет


void __fastcall TForm1::Button1Click(TObject *Sender)
{
float p; // вес в фунтах
float kg; // вес в килограммах

if (Edit1->Text.Length() != 0)
{
p = StrToFloat(Edit1->Text);
kg = p * 0.454; // 1 фунт = 454 гр.
Label2->Font->Color = clNavy;
Label2->Caption = FloatToStr(p) + " ф. = " +
FloatToStrF(kg,ffGeneral,6,2) + " кг.";
}
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 73

else
{
Label2->Font->Color = clMaroon; // темно-красный
Label2->Caption = "Введите исходные данные";
Edit1->SetFocus(); // переместить курсор в поле Edit1
}
}

Edit
Компонент Edit обеспечивает ввод и редактирование текста. Значок компо-
нента находится на вкладке Standard (рис. 3.3), свойства приведены в
табл. 3.3.

Рис. 3.3. Значок компонента Edit

Òàáëèöà 3.3. Ñâîéñòâà êîìïîíåíòà Edit

Свойство Îïèñàíèå
Name Имя (идентификатор) компонента
Text Текст, находящийся в поле ввода и редактирования
SelStart Номер первого символа выделенного фрагмента текста. Если фраг-
мент не выделен, то положение курсора — в поле редактирования
SelLength Количество символов в выделенном фрагменте
SelText Выделенный текст
Left Расстояние от левой границы компонента до левой границы формы
Top Расстояние от верхней границы компонента до верхней границы
формы
Height Высота
Width Ширина
Font Шрифт, используемый для отображения текста
ParentFont Признак наследования компонентом характеристик шрифта формы,
на которой находится компонент. Если значение свойства равно
true, то при изменении свойства Font формы автоматически меня-
ется значение свойства Font компонента
74 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 3.3 (îêîí÷àíèå)

Свойство Îïèñàíèå
Enabled Используется для ограничения возможности изменения текста в
поле редактирования. Если значение свойства равно false, то
текст в поле редактирования изменить нельзя
Visible Позволяет скрыть компонент (false) или сделать его видимым
(true)

В поле компонента Edit отображаются все символы, которые пользователь


набирает на клавиатуре. Вместе с тем программист может, создав функцию
обработки события KeyPress, запретить ввод (отображение) некоторых сим-
волов.
Следующая программа (ее форма приведена на рис. 3.4, а текст в лис-
тинге 3.2) демонстрирует использование компонента Edit для ввода данных
различного типа. Программа спроектирована таким образом, что в поле
Текст можно ввести любой символ, в поле Целое число — только цифры и
знак "–" (если это первый символ), в поле Дробное число — цифры, знак "–"
и символ-разделитель. Следует обратить внимание: вне зависимости от того,
какую клавишу (точку или запятую) нажмет пользователь, в поле редактиро-
вания появится "правильный" символ — запятая (символ, который следует
использовать при вводе дробных чисел, задается в настройках операционной
системы).

Рис. 3.4. Форма программы Компонент Edit

Листинг 3.2. Компонент Edit

//--- Нажатие клавиши в поле Текст -----------------------------------


void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if (Key == VK_RETURN) // клавиша <Enter>
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 75

{
Edit2->SetFocus(); // переместить фокус (курсор) в поле Edit2
}
}

//--- Нажатие клавиши в поле Целое число ----------------------------


void __fastcall TForm1::Edit2KeyPress(TObject *Sender, char &Key)
{
if ( isdigit(Key) ) // цифра
{
return;
}

switch (Key) {
case '-':
// "минус" может быть только первым символом
if ( Edit2->SelStart != 0)
Key =0;
break;
case VK_RETURN:
Edit3->SetFocus();
break;
case VK_BACK:
break;
default:
Key = 0;
}
}

//--- Нажатие клавиши в поле Дробное число ---------------------------


void __fastcall TForm1::Edit3KeyPress(TObject *Sender, char &Key)
{
if ( isdigit(Key) ) // цифра
{
return;
}

if (( Key == ',') || ( Key == '.') )


{
Key = DecimalSeparator;
if ((Edit3->Text.Pos(DecimalSeparator) != 0))
{
Key = 0;
}
76 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

return;
}

switch (Key) {
case '-':
// "минус" может быть только первым символом
if ( Edit3->SelStart != 0)
Key =0;
break;

case VK_BACK: // <Backspace>


break;

default:
Key = 0;
}
}

Button
Компонент Button представляет собой командную кнопку. Значок компонен-
та находится на вкладке Standard (рис. 3.5), свойства приведены в табл. 3.4.

Рис. 3.5. Значок компонента Button

Òàáëèöà 3.4. Ñâîéñòâà êîìïîíåíòà Button

Свойство Îïèñàíèå

Name Имя (идентификатор) компонента


Caption Текст на кнопке
Left Расстояние от левой границы кнопки до левой границы формы
Top Расстояние от верхней границы кнопки до верхней границы формы

Height Высота кнопки

Width Ширина кнопки


Ãëàâà 3. Áàçîâûå êîìïîíåíòû 77

Òàáëèöà 3.4 (îêîí÷àíèå)

Свойство Îïèñàíèå
Enabled Признак доступности кнопки. Если значение свойства равно true, то
кнопка доступна. Если значение свойства равно false, то кнопка не
доступна (в результате щелчка на кнопке событие Click не возни-
кает)
Visible Позволяет скрыть кнопку (false) или сделать ее видимой (true)
Hint Подсказка — текст, который появляется рядом с указателем мыши
при позиционировании указателя на командной кнопке (чтобы текст
появился, значение свойства ShowHint должно быть true)
ShowHint Разрешает (true) или запрещает (false) отображение подсказки
при позиционировании указателя на кнопке

Следующая программа (ее форма приведена на рис. 3.6, а текст в листин-


ге 3.3) демонстрирует использование компонента Button. Программа пере-
считывает скорость из миль/час в км/час. Расчет и отображение результата
выполняет функция обработки события Click на кнопке OK. Следует обра-
тить внимание, что кнопка OK доступна только в том случае, если в поле
редактирования есть данные (хотя бы одна цифра). Управляет доступностью
кнопки функция обработки события Change компонента Edit1. Функция кон-
тролирует количество символов, которое находится в поле редактирования, и,
если в поле нет ни одной цифры, присваивает значение false свойству
Enabled и тем самым делает кнопку недоступной.

Рис. 3.6. Форма программы Мили-километры

Листинг 3.3. Мили-километры

// конструктор формы
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
78 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

{
/* т. к. поле Edit1 пустое (пользователь
еще не ввел исходные данные), то
сделаем кнопку Ok недоступной */
Button1->Enabled = false;
}

// Щелчок на кнопке Ok
void __fastcall TForm1::Button1Click(TObject *Sender)
{
float m; // скорость миль/час
float k; // скорость км/час

// кнопка Пересчет доступна только в том случае,


// если в поле Edit1 есть данные. Поэтому
// наличие в поле информации можно не проверять.
m = StrToFloat(Edit1->Text);
k = m * 1.6094;
Label2->Caption = FloatToStrF(m,ffGeneral,5,2) +
" m/h — это " +
FloatToStrF(k,ffGeneral,5,2) + " км/час";
}

// Нажатие клавиши в поле Edit1


void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
// код запрещенного символа заменим нулем, в результате чего
// символ в поле редактирования не появится

// Key — код нажатой клавиши


// проверим, является ли символ допустимым
if ((Key >= '0') && (Key <= '9')) // цифра
return;

// глобальная переменная DecimalSeparator


// содержит символ, используемый в качестве разделителя
// при записи дробных чисел
if ((Key == '.')|| (Key == ','))
{
Key = DecimalSeparator;
if ((Edit1->Text).Pos(DecimalSeparator) != 0)
Key = 0; // разделитель уже введен
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 79

return;
}

if (Key == VK_BACK) // клавиша <Backspace>


return;

if (Key == VK_RETURN) // клавиша <Enter>


{
Button1->SetFocus();
return;
}

// остальные клавиши запрещены


Key = 0; // не отображать символ
}

// Содержимое поля Edit1 изменилось


void __fastcall TForm1::Edit1Change(TObject *Sender)
{
// проверим, есть ли в поле Edit1 исходные данные
if ( (Edit1->Text).Length() == 0)
Button1->Enabled = false; // теперь кнопка Ok недоступна
else Button1->Enabled = true; // теперь кнопка Ok доступна

Label2->Caption = "";
}

CheckBox
Компонент CheckBox представляет собой переключатель, который может на-
ходиться в одном из двух состояний: выбранном или невыбранном. Часто
вместо "выбранный" говорят "установленный", а вместо "невыбранный" —
"сброшенный". Рядом с переключателем обычно находится поясняющий
текст. Значок компонента находится на вкладке Standard (рис. 3.7), свойства
компонента приведены в табл. 3.5.

Рис. 3.7. Значок компонента CheckBox


80 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 3.5. Ñâîéñòâà êîìïîíåíòà CheckBox

Свойство Описание

Name Имя (идентификатор) компонента


Caption Комментарий (текст, который находится справа от флажка)
Checked Состояние, внешний вид флажка:
если флажок установлен (в квадратике есть "галочка"), то значение
Checked равно true;
если флажок сброшен (нет "галочки"), то значение Checked равно
false

State Состояние флажка. В отличие от свойства Checked, позволяет


различать установленное, сброшенное и промежуточное состоя-
ния.
Состояние флажка определяет одна из констант:
cbChecked (установлен);
cbGrayed (серый, неопределенное состояние);
cbUnChecked (сброшен)

AllowGrayed Свойство определяет, может ли флажок быть в промежуточном


состоянии:
если значение AllowGrayed равно false, то флажок может быть
только установленным или сброшенным;
если значение AllowGrayed равно true, то допустимо промежуточ-
ное состояние

Left Расстояние от левой границы флажка до левой границы формы

Top Расстояние от верхней границы флажка до верхней границы формы

Height Высота поля вывода поясняющего текста

Width Ширина поля вывода поясняющего текста

Font Шрифт, используемый для отображения поясняющего текста

ParentFont Признак наследования характеристик шрифта родительской формы

Следующая программа (ее форма приведена на рис. 3.8, а текст в листин-


ге 3.4) демонстрирует использование компонента CheckBox. Программа по-
зволяет посчитать цену металлопластикового окна. Значения свойств компо-
нентов CheckBox приведены в табл. 3.6.
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 81

Рис. 3.8. Форма программы Стеклопакет

Òàáëèöà 3.6. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ CheckBox

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


CheckBox1 Caption подоконник
Checked true
CheckBox2 Caption монтаж
Checked true

Листинг 3.4. Стеклопакет

// Щелчок на кнопке OK -----------------------------------


void __fastcall TForm1::Button1Click(TObject *Sender)
{
float w,h; // размер окна
float sum; // сумма

w = StrToFloat(Edit1->Text);
h = StrToFloat(Edit2->Text);

sum = (w/100 * h/100) * 2800; // 2800 — цена за 1 кв.м.

if ( CheckBox1->Checked)
// установлен флажок "подоконник"
sum = sum + 750;

if (CheckBox2->Checked)
// установлен флажок "монтаж"
sum = sum + 1500;
82 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Label3->Caption = FloatToStrF(sum,ffCurrency,6,2);

}
//---- Щелчок на переключателе Подоконник -----------------
void __fastcall TForm1::CheckBox1Click(TObject *Sender)
{
// изменилось состояние переключателя,
// поэтому сумма, отображаемая в поле Label3,
// не соответствует исходным данным
Label3->Caption = "";
}

//---- Щелчок на переключателе Монтаж ---------------


void __fastcall TForm1::CheckBox2Click(TObject *Sender)
{
Label3->Caption = "";
}

RadioButton
Компонент RadioButton представляет собой кнопку (переключатель), со-
стояние которой зависит от состояния других компонентов RadioButton, на-
ходящихся на форме. Обычно компоненты RadioButton объединяют в груп-
пу (достигается это путем размещения нескольких компонентов в поле ком-
понента GroupBox). В каждый момент времени только одна из кнопок группы
может находиться в выбранном состоянии (возможна ситуация, когда ни одна
из кнопок не выбрана). Состояние кнопок, принадлежащих одной группе, не
зависит от состояния кнопок, принадлежащих другой группе. Значок компо-
нента RadioButton находится на вкладке Standard (рис. 3.9), его свойства
приведены в табл. 3.7.

Рис. 3.9. Значок компонента RadioButton

Òàáëèöà 3.7. Ñâîéñòâà êîìïîíåíòà RadioButton

Свойство Описание
Name Имя (идентификатор) компонента
Caption Комментарий (текст, который находится справа от кнопки)
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 83

Òàáëèöà 3.7 (îêîí÷àíèå)

Свойство Описание
Checked Состояние. Определяет вид кнопки:
true — кнопка выбрана;
false — кнопка не выбрана (выбрана другая кнопка группы)
Left Расстояние от левой границы флажка до левой границы формы
Top Расстояние от верхней границы флажка до верхней границы формы
Height Высота поля вывода поясняющего текста
Width Ширина поля вывода поясняющего текста
Font Шрифт, используемый для отображения поясняющего текста
ParentFont Признак наследования характеристик шрифта родительской формы

Следующая программа (ее форма приведена на рис. 3.10, а текст в листин-


ге 3.5) демонстрирует использование компонента RadioButton. Программа
вычисляет цену жалюзи в зависимости от размера и материала, из которого
они изготовлены. Значения свойств компонентов RadioButton приведены в
табл. 3.8.

Рис. 3.10. Форма программы Жалюзи

Òàáëèöà 3.8. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ RadioButton

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


RadioButton1 Caption алюминий
Checked true
84 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 3.8 (îêîí÷àíèå)

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


RadioButton2 Caption пластик
Checked false

Листинг 3.5. Жалюзи

//-----Щелчок на кнопке OK --------------------------------------------


void __fastcall TForm1::Button1Click(TObject *Sender)
{
float w,h,s; // ширина, высота и площадь
float c; // цена за 1 кв.м.
float sum; // сумма
AnsiString st; // сообщение

w = StrToFloat(Edit1->Text);
h = StrToFloat(Edit2->Text);
s = w * h / 10000;

if ( RadioButton1->Checked )
c = 360; // выбран переключатель "алюминий"
else
c = 180; // выбран переключатель "пластик"

sum = s * c;

st = "Размер: " + Edit1->Text + "x" + Edit2->Text +


"см\nМатериал: ";

if ( RadioButton1->Checked )
st = st + "алюминий";
else
st = st + "пластик";

st = st + "\nСумма: " + FloatToStrF(sum, ffCurrency, 6,2);

Label3->Caption = st;

}
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 85

ComboBox
Компонент ComboBox представляет собой комбинацию поля редактирования
и списка, что дает возможность ввода данных путем набора на клавиатуре
или выбором из списка. Значок компонента находится на вкладке Standard
(рис. 3.11), свойства приведены в табл. 3.9.

Рис. 3.11. Значок компонента ComboBox

Òàáëèöà 3.9. Ñâîéñòâà êîìïîíåíòà ComboBox

Свойство Описание
Name Имя (идентификатор) компонента
Style Вид компонента:
csDropDown — поле ввода и раскрывающийся список (данные
можно ввести в поле редактирования или выбрать в списке);
csDropDownList — только раскрывающийся список;
csSimple — только поле редактирования
Text Текст, находящийся в поле ввода/редактирования
Items Элементы списка — массив строк
Count Количество элементов списка
ItemIndex Номер элемента, выбранного в списке (элементы нумеруются
с нуля). Если ни один из элементов списка не выбран, то значе-
ние свойства равно –1
Sorted Признак необходимости автоматической сортировки (true)
списка после добавления очередного элемента
DropDownCount Количество элементов, отображаемых в раскрытом списке.
Если количество элементов списка больше DropDownCount, то
появляется вертикальная полоса прокрутки
Left Расстояние от левой границы компонента до левой границы
формы
Top Расстояние от верхней границы компонента до верхней грани-
цы формы
Height Высота компонента (поля ввода/редактирования)
86 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 3.9 (îêîí÷àíèå)

Свойство Описание
Width Ширина компонента
Font Шрифт, используемый для отображения элементов списка
ParentFont Признак наследования свойств шрифта родительской формы

Список, отображаемый в поле компонента, можно сформировать во время


создания формы или во время работы программы. Чтобы сформировать спи-
сок во время создания формы, надо выбрать свойство Items, щелкнуть на на-
ходящейся в поле значения свойства кнопке и в окне String List Editor вве-
сти элементы списка (рис. 3.12).

Рис. 3.12. Формирование списка компонента во время разработки формы

Чтобы сформировать список во время работы программы (добавить в список


элемент), надо применить метод Add к свойству Items.
Например:
ComboBox1->Items->Add("1 мес. — 4.25%");
ComboBox1->Items->Add("3 мес. — 4.5%");
ComboBox1->Items->Add("6 мес. — 6.75%");
ComboBox1->Items->Add("1 год — 7.75%");

Следующая программа (ее форма приведена на рис. 3.13) демонстрирует ис-


пользование компонента ComboBox. Срок вклада выбирается в списке
ComboBox. Настройку компонента ComboBox выполняет конструктор формы.
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 87

Функция обработки события Click на кнопке Button1 и функция обработки


события Create формы приведены в листинге 3.6.

Рис. 3.13. Форма программы Доход по вкладу

Листинг 3.6. Доход по вкладу

// Конструктор формы
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
ComboBox1->Style = csDropDownList;
ComboBox1->Items->Add("1 мес. — 4.25%");
ComboBox1->Items->Add("3 мес. — 4.5%");
ComboBox1->Items->Add("6 мес. — 6.75%");
ComboBox1->Items->Add("1 год — 7.75%");
}

// Щелчок на кнопке OK
void __fastcall TForm1::Button1Click(TObject *Sender)
{
float sum; // сумма
int period; // срок вклада
float percent; // процентная ставка
float profit; // доход
float sum2; // сумма в конце срока вклада

if (( Edit1->Text.Length() != 0 ) && ( ComboBox1->ItemIndex != -1 ))


{
sum = StrToFloat(Edit1->Text);
88 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

switch ( ComboBox1->ItemIndex )
{
case 0: percent = 4.25;
period = 1;
break;
case 1: percent = 4.5;
period = 3;
break;
case 2: percent = 6.75;
period = 6;
break;
case 3: percent = 7.75;
period = 12;
break;
}

// расчет
profit = sum * percent / 100 /12 * period;
sum2 = sum + profit;

Label3->Font->Color = clWindowText;
Label3->Caption =
"Сумма: " + FloatToStrF(sum, ffCurrency, 6,2) +
"\nСрок: " + IntToStr(period) + " мес." +
"\nПроцентная ставка (годовых): " +
FloatToStrF(percent, ffGeneral, 5,2) + "%" +
"\nДоход: " + FloatToStrF(profit, ffCurrency, 6,2) +
"\nСумма в конце срока вклада: " +
FloatToStrF(sum2, ffCurrency, 6,2);
}
else
{
Label3->Font->Color = clMaroon;
Label3->Caption = "Надо ввести исходные данные";
}
}

ListBox
Компонент ListBox представляет собой список, в котором можно выбрать
нужный элемент. Значок компонента находится на вкладке Standard
(рис. 3.14), свойства приведены в табл. 3.10.
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 89

Рис. 3.14. Значок компонента ListBox

Òàáëèöà 3.10. Ñâîéñòâà êîìïîíåíòà ListBox

Свойство Описание

Name Имя (идентификатор) компонента


Items Элементы списка
Count Количество элементов списка
Sorted Признак необходимости автоматической сортировки (true) списка
после добавления очередного элемента
ItemIndex Номер выбранного элемента (элементы списка нумеруются с нуля).

Если ни один из элементов не выбран, то значение свойства равно


–1 (минус единице)
Left Расстояние от левой границы списка до левой границы формы
Top Расстояние от верхней границы списка до верхней границы формы
Height Высота поля списка
Width Ширина поля списка
Font Шрифт, используемый для отображения элементов списка
ParentFont Признак наследования свойств шрифта родительской формы

Список, отображаемый в поле компонента, можно сформировать как во вре-


мя создания формы, так и во время работы программы. Чтобы сформировать
список во время создания формы, надо выбрать свойство Items, щелкнуть на
находящейся в поле значения свойства кнопке и в окне String List Editor
ввести элементы списка. Формирование списка во время работы программы
обеспечивает метод Add свойства Items.
Следующая программа (ее окно приведено на рис. 3.15) демонстрирует ис-
пользование компонента ListBox. Программа позволяет просмотреть фото-
графии (jpg-файлы).
Форма программы Просмотр иллюстраций приведена на рис. 3.16, объявле-
ние класса TForm1 и модуль формы — соответственно в листингах 3.7 и 3.8.
90 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 3.15. Окно программы Просмотр иллюстраций

Обратите внимание: в объявлении класса TForm1 добавлено объявление


функции FillListBox и переменной Path.
Функция FillListBox формирует список jpg-файлов (заполняет список ком-
понента ListBox), находящихся в выбранном пользователем каталоге (его
имя находится в переменной Path). Объявление функции помещено в объяв-
ление класса для того, чтобы у функции был доступ к компоненту ListBox.
В начале работы программы функцию FillListBox вызывает конструктор
формы, для того чтобы сформировать список иллюстраций, находящихся
в каталоге программы.
Функция обработки события Click на кнопке Папка путем вызова функции
SelectDirectory отображает стандартное окно Выбор папки. Затем (если
пользователь выберет папку и сделает щелчок на кнопке OK) она вызывает
функцию FillListBox, которая формирует фотографии.
Фотография отображается в поле компонента Image (см. далее). Для того что-
бы иллюстрация отображалась без искажения, свойству AutoSise компонен-
та Image надо присвоить значение false, а свойству Proportional — true.
Загрузку выбранной иллюстрации в компонент Image осуществляет функция
обработки события Click компонента ListBox (это событие происходит в ре-
зультате щелчка на элементе списка, а также при перемещении указателя те-
кущего элемента списка с помощью клавиш управления курсором).
Следует обратить внимание: в текст модуля формы надо поместить директи-
ву #include <filectrl.hpp>. Она обеспечивает подключение модуля
filectrl и, соответственно, доступ к функциям FindFirst, FindNext. Мо-
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 91

дуль jpeg, подключаемый директивой #include <jpeg.hpp>, обеспечивает


отображение jpg-иллюстраций.

Рис. 3.16. Форма программы Просмотр иллюстраций

Листинг 3.7. Просмотр иллюстраций (Unit1.h)

class TForm1 : public TForm


{
__published: // IDE-managed Components

TListBox *ListBox1;
TImage *Image1;
TButton *Button1;
void __fastcall ListBox1Click(TObject *Sender);
void __fastcall Button1Click(TObject *Sender);

private: // User declarations


__fastcall int FillListBox( ); // заполняет список компонента ListBox
AnsiString Path; // каталог, в котором находятся иллюстрации

public: // User declarations


__fastcall TForm1(TComponent* Owner);
};
92 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Листинг 3.8. Просмотр иллюстраций (Unit1.cpp)

#include <jpeg.hpp>
#include <filectrl.hpp>

TForm1 *Form1;

// Конструктор
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// т. к. значение переменной Path равно "пустая строка",
// то будет сформирован список иллюстраций текущего
// каталога, т. е. того, в котором находится exe-файл
this->FillListBox();
}

// Сформировать список jpg-файлов


__fastcall TForm1::FillListBox( )
{
TSearchRec SearchRec; // результат поиска файла
int r;

r = FindFirst(Path + "*.jpg",faAnyFile,SearchRec);
if (r == 0)
{
// в каталоге Path есть по крайней мере один jpg-файл
ListBox1->Items->Clear();
ListBox1->Items->Add(SearchRec.Name);
while ( FindNext(SearchRec) == 0 )
{
ListBox1->Items->Add(SearchRec.Name);
};

ListBox1->ItemIndex = 0;

// отобразить иллюстрацию
Image1->Picture->LoadFromFile(Path +
ListBox1->Items->Strings[ListBox1->ItemIndex]);

return ListBox1->Items->Count;
}
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 93

else
return -1;
}

// Щелчок в поле компонента ListBox (на имени файла)


void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
AnsiString FileName;

FileName = Path + ListBox1->Items->Strings[ListBox1->ItemIndex];


Image1->Picture->LoadFromFile(FileName);

// Щелчок на кнопке Папка


void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (SelectDirectory("Выберите каталог", "",Path ) )
{
Path = Path + "\\";
Form1->Caption = "Просмотр иллюстраций — " + Path;
FillListBox();
}
}

Memo
Компонент Memo представляет собой элемент редактирования многострочного
текста. Значок компонента находится на вкладке Standard (рис. 3.17), свой-
ства приведены в табл. 3.11.

Рис. 3.17. Значок компонента Memo

Òàáëèöà 3.11. Ñâîéñòâà êîìïîíåíòà Memo

Свойство Описание
Name Имя (идентификатор) компонента
Text Текст, находящийся в поле Memo. Рассматривается как единое целое
94 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 3.11 (îêîí÷àíèå)

Свойство Описание

Lines Массив строк, соответствующий содержимому поля. Доступ к строке


осуществляется по номеру. Строки нумеруются с нуля
Left Расстояние от левой границы поля до левой границы формы
Top Расстояние от верхней границы поля до верхней границы формы
Width Ширина поля
Height Высота поля
Font Шрифт, используемый для отображения вводимого текста
ParentFont Признак наследования свойств шрифта родительской формы
ReadOnly Разрешает (false) или запрещает (true) редактирование текста,
находящегося в поле редактирования
ScrollBars Задает отображаемые полосы прокрутки:
ssVertical — только вертикальная;
ssHorisontal — только горизонтальная;
ssBoth — вертикальная и горизонтальная;
ssNone— полосы прокрутки не отображать
Modified Индикатор: true — текст изменен

Следующая программа (ее форма приведена на рис. 3.18, а текст в листин-


ге 3.9) демонстрирует использование компонента Memo для отображения тек-
ста, который находится в файле. Значения свойств компонента Memo приве-
дены в табл. 3.12. Так как значение свойства Align равно alClient, то при

Рис. 3.18. Форма программы Компонент Memo


Ãëàâà 3. Áàçîâûå êîìïîíåíòû 95

изменении размера окна автоматически будет изменяться размер компонента


Memo, причем он будет занимать всю клиентскую область окна (внутреннюю
область окна называют клиентской областью). Загрузку текста из файла
kurs.txt выполняет функция обработки события Activate формы. Функция
обработки события CloseQuery, которое возникает в результате щелчка на
кнопке Закрыть окно или выбора в системном меню команды Закрыть,
проверяет, внесены ли в текст какие-либо изменения, и если текст изменен
(значение свойства Modified компонента Memo в этом случае равно true), то
предлагает сохранить его (сообщение выводит функция MessageDlg).

Òàáëèöà 3.12. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà Memo

Свойство Значение
Align alClient
ScrollBars ssVertical

Листинг 3.9. Компонент Memo

// Событие Activate происходит сразу после того,


// как окно появится на экране
void __fastcall TForm1::FormActivate(TObject *Sender)
{
// загрузить в компонент Memo1 текст из файла
try
{
Memo1->Lines->LoadFromFile("kurs.txt");
} catch (EFOpenError &e) {
ShowMessage(e.Message);
}
}

// Щелчок на кнопке Закрыть (в заголовке окна)


void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
// Если присвоить значение false переменной CanClose,
// то окно не будет закрыто. По умолчанию значение
// CanClose равно true.

int r; // идентификатор кнопки, нажатой


// пользователем, в окне сообщения
96 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

if (Memo1->Modified)
{
// содержимое поля редактирования изменено
r = MessageDlg("Сохранить изменения?", mtWarning,
TMsgDlgButtons() << mbYes << mbNo << mbCancel,0);

switch ( r )
{
case mrYes: // записать текст в файл
Memo1->Lines->SaveToFile("kurs.txt");
break;
case mrCancel: // продолжить работу с программой
CanClose = false;
}
}
}

Timer
Компонент Timer генерирует последовательность событий Timer, он являет-
ся невизуальным, т. е. во время работы программы на форме не отображает-
ся. Значок компонента находится на вкладке System (рис. 3.19), свойства
приведены в табл. 3.13.

Рис. 3.19. Значок компонента Timer

Òàáëèöà 3.13. Ñâîéñòâà êîìïîíåíòà Timer

Свойство Описание
Interval Период возникновения события Timer. Задается в миллисекундах
Enabled Разрешает (true) или запрещает (false) генерацию события Timer

Программа Секундомер (ее форма приведена на рис. 3.20, а текст в листин-


ге 3.10) демонстрирует использование компонента Timer. В процессе созда-
ния формы свойству Interval компонента Timer1 надо присвоить значение
500 (таймер будет генерировать событие Timer каждые полсекунды). Кнопка
Button1 обеспечивает как запуск секундомера, так и его останов. В начале
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 97

работы программы значение свойства Enabled компонента Timer1 равно


false, поэтому таймер события Timer не генерирует.

Рис. 3.20. Форма программы Секундомер

Функция обработки события Click на кнопке Button1 проверяет состояние


таймера, и если таймер не работает, то запускает его (присваивает значение
true свойству Enabled).
Функция обработки события Timer, которое возникает с периодом 0,5 секунд,
инвертирует значение свойства Visible компонента Label2 (в результате
двоеточие мигает), и если двоеточие отображается, то увеличивает счетчик
времени и выводит в поле компонентов Label1 и Label3 значения счетчиков
минут и секунд. Следует обратить внимание: объявления счетчиков минут и
секунд (переменных s и m) надо поместить в секцию private объявления
формы (файл Unit1.h). Если секундомер работает, то щелчок на кнопке
Button1 останавливает секундомер.
Функция обработки события Click на кнопке Сброс (эта кнопка доступна
только в том случае, если секундомер остановлен) сбрасывает показания и
обнуляет счетчики секунд и минут.

Листинг 3.10. Секундомер

// --- Щелчок на кнопке Старт/Стоп--------------------------------------


void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (Timer1->Enabled) {
// Щелчок на кнопке Стоп
Timer1->Enabled = false; // остановить таймер
Button1->Caption = "Старт";
Button2->Enabled = true;
Label2->Visible = true;
}
else {
// Щелчок на кнопке Старт
Timer1->Enabled = true; // пустить таймер
Button1->Caption = "Стоп";
98 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Button2->Enabled = false;
}
}

// --- Cигнал от таймера (событие Timer) ------------------------------


void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// таймер генерирует событие Timer каждые 0,5 с
// показать/скрыть двоеточие
Label2->Visible = ! Label2->Visible;

if ( ! Label2->Visible)
return;

// в эту точку попадаем каждую секунду


if (s == 59)
{
m++;
Label1->Caption = IntToStr(m);
s = 0;
}
else
s++;

// отобразить секунды
if (s < 10)
Label3->Caption = "0"+ IntToStr(s);
else
Label3->Caption = IntToStr(s);
}

// --- Щелчок на кнопке Сброс ----------------------------------------


void __fastcall TForm1::Button2Click(TObject *Sender)
{
s = 0;
m = 0;
Label1->Caption = "00";
Label3->Caption = "00";
}

Panel
Компонент Panel представляет собой панель, на поверхность которой можно
поместить другие компоненты. Обычно панель используют для привязки
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 99

компонентов к границе окна (при изменении размера окна компоненты, на-


ходящиеся на панели, не меняют своего положения относительно границы
окна, к которой "привязана" панель). Значок компонента находится на вклад-
ке Standard (рис. 3.21), описание некоторых свойств приведено в табл. 3.14.

Рис. 3.21. Значок компонента Panel

Òàáëèöà 3.14. Ñâîéñòâà êîìïîíåíòà Panel

Свойство Описание
Align Определяет границу формы, к которой привязана (прикреплена)
панель. Панель может быть прикреплена к левой (alLeft), правой
(alRight), верхней (alTop) или нижней (alBottom) границе
BevelOuter Внешняя "фаска" панели.
Если значение свойства равно bvNone, то фаска не отображается
и поверхность панели находится на одном уровне с поверхностью
формы.
Если значение свойства равно bvLowered, то поверхность панели
притоплена.
Если значение свойства равно bvRaised, то поверхность панели
выступает над поверхностью формы
Enabled Свойство позволяет сделать недоступными (false) все компонен-
ты, которые находятся на панели

На рис. 3.22 приведено окно программы Просмотр иллюстраций, в котором


кнопки управления (SpeedButton) находятся на панели (значения свойств
компонента Panel приведены в табл. 3.15).

Òàáëèöà 3.15. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà Panel

Свойство Значение
Align alBottom
BevelOuter bvNone

Панель привязана к нижней границе окна, поэтому при изменении размера


окна (даже в том случае, если пользователь развернет окно программы на
100 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

весь экран) панель и, следовательно, кнопки всегда будут находиться в ниж-


ней его части. Обратите внимание: программа спроектирована так, что вне
зависимости от ширины окна кнопки управления всегда находятся в центре
панели. Изменяет положение кнопок на панели процедура обработки события
Resize панели (листинг 3.11), которое возникает при изменении ширины ок-
на программы.

Рис. 3.22. Окно программы Просмотр иллюстраций

Листинг 3.11. Обработка события Resize

// Изменилась ширина панели.


// Т. к. панель привязана к нижней границе формы,
// то при изменении ширины окна меняется и ширина панели.
void __fastcall TForm1::Panel1Resize(TObject *Sender)
{
// разместить кнопки в центре панели
SpeedButton2->Left = Panel1->Width / 2;
SpeedButton3->Left = Panel1->Width / 2 — SpeedButton3->Width;
}

ControlBar
Компонент ControlBar представляет собой так называемую панель инстру-
ментов, на поверхность которой можно поместить другие компоненты.
Обычно панель инструментов находится в верхней части окна, а на ее по-
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 101

верхности размещены командные кнопки. Значок компонента находится на


вкладке Additional (рис. 3.23), некоторые свойства приведены в табл. 3.16.

Рис. 3.23. Значок компонента ControlBar

Òàáëèöà 3.16. Ñâîéñòâà êîìïîíåíòà ControlBar

Свойство Описание

Align Определяет границу формы, к которой привязана (прикре-


плена) панель. Панель может быть прикреплена к левой
(alLeft), правой (alRight), верхней (alTop) или нижней
(alBottom) границе
AutoSize Если значение свойства равно true, то высота панели ин-
струментов автоматически устанавливается равной высоте
командных кнопок, находящихся на панели
DrawingStyle Способ закраски панели: градиент (dsGradient) или
сплошная закраска одним цветом (dsNormal)
GradientDirection Направление градиента закраски: gdVertical — по верти-
кали, gdHorizontal — по горизонтали. Цвета градиентной
закраски определяют свойства GradientStartColor и
GradientEndColor

SpeedButton
Компонент SpeedButton представляет собой командную кнопку, на которой
отображается картинка. Обычно компоненты SpeedButton размещают на по-
верхности компонента Panel или TollBar. Значок компонента находится на
вкладке Standard (рис. 3.24), свойства приведены в табл. 3.17.

Рис. 3.24. Значок компонента SpeedButton


102 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 3.17. Ñâîéñòâà êîìïîíåíòà SpeedButton

Свойство Описание

Name Имя (идентификатор) компонента


Glyph Битовый образ, в котором находятся картинки для каждого из воз-
можных состояний кнопки (доступна, недоступна, нажата, зафикси-
рована)
NumGlyphs Количество картинок в битовом образе Glyph
Flat Определяет вид кнопки (наличие границы). Если значение свойства
равно true, то граница кнопки появляется только при позициони-
ровании указателя мыши на кнопке
GroupIndex Идентификатор группы кнопок. Кнопки, имеющие одинаковый иден-
тификатор группы, работают подобно переключателям
(RadioButton): нажатие одной из кнопок группы вызывает срабаты-
вание других кнопок этой группы.
Чтобы кнопку можно было зафиксировать, значение свойства
GroupIndex не должно быть равно нулю
Down Идентификатор состояния кнопки. Изменить значение свойства
можно, если значение свойства GroupIndex не равно 0
AllowAllUp Свойство определяет возможность отжатия кнопки. Если кнопка
нажата и значение свойства равно true, то кнопку можно отжать
Left Расстояние от левой границы кнопки до левой границы формы
Top Расстояние от верхней границы кнопки до верхней границы формы

Height Высота кнопки

Width Ширина кнопки


Enabled Признак доступности кнопки. Если значение свойства равно true,
то кнопка доступна. Если значение свойства равно false, то кнопка
не доступна
Visible Позволяет скрыть кнопку (false) или сделать ее видимой (true)
Hint Подсказка — текст, который появляется рядом с указателем мыши
при позиционировании указателя на командной кнопке
ShowHint Разрешает (true) или запрещает (false) отображение подсказки
при позиционировании указателя мыши на кнопке

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


картинки для каждого из возможных состояний кнопки (доступна, недоступ-
на, нажата, зафиксирована). Структура битового образа для компонента
SpeedButton приведена на рис. 3.25.
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 103

Рис. 3.25. Структура битового образа Рис. 3.26. Битовый образ для кнопки Дальше
компонента SpeedButton

Чтобы задать битовый образ, надо в окне Object Inspector выбрать свойство
Glypht, сделать щелчок на кнопке с тремя точками, в окне Picture Editor
щелкнуть на кнопке Load и в окне Load Picture выбрать bmp-файл, в кото-
ром находится битовый образ. В качестве примера на рис. 3.26 приведен би-
товый образ для кнопки Дальше. Следует обратить внимание на то, что ле-
вый нижний пиксел картинки задает "прозрачный" цвет — элементы рисунка,
окрашенные этим цветом, на поверхности кнопки не отображаются. Также
необходимо отметить, что bmp-файл, из которого был загружен битовый об-
раз во время разработки формы, во время работы программы не нужен.
На рис. 3.27 приведена форма программы Просмотр иллюстраций, в кото-
рой для выбора папки, перехода к следующей иллюстрации и возврата к пре-

Рис. 3.27. Форма программы Просмотр иллюстраций


104 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

дыдущей используются кнопки SpeedButton. Обратите внимание, что снача-


ла на форму нужно поместить компонент Panel и настроить его (присвоить
значение свойству Align), после чего на поверхность панели нужно помес-
тить командные кнопки. Компонент Image настраивается последним. Значе-
ния свойств компонентов приведены в табл. 3.18.

Òàáëèöà 3.18. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


Panel1 Align alBottom
BevelOuter bvNone
Height 32
SpeedButton1 Width 49
Height 22
Flat true
Glyph

NumGlyphs 1
Hint Выбор папки
ShowHint true
SpeedButton2 Width 49
Height 22
Flat true
Glyph

NumGlyphs 2
Hint Следующая
ShowHint true
SpeedButton3 Width 49
Height 22
Flat true
Glyph

NumGlyphs 2
Hint Предыдущая
ShowHint true
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 105

Òàáëèöà 3.18 (îêîí÷àíèå)

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


Image1 Proportional true
Align alClient
AlignWithMargins true

StatusBar
Компонент StatusBar представляет собой область вывода служебной инфор-
мации (область состояния). Обычно область состояния разделена на несколь-
ко частей, которые называют панелями. Значок компонента находится на
вкладке Win32 (рис. 3.28), свойства приведены в табл. 3.19.

Рис. 3.28. Значок компонента StatusBar

Òàáëèöà 3.19. Ñâîéñòâà êîìïîíåíòà StatusBar

Свойство Описание
Panels Коллекция объектов типа TStatusPanel (табл. 3.20), каждый из
которых представляет собой панель, отображаемую в области
состояния
SimpleText Текст, который отображается в поле компонента, если значение
свойства SimplePanel равно true
SimplePanel Тип компонента. Если значение свойства равно true, то в поле
компонента отображается текст, заданный значением свойства
SimpleText. Если значение свойства равно false, то в поле
компонента отображаются панели

Òàáëèöà 3.20. Ñâîéñòâà îáúåêòà TStatusPanel

Свойство Описание
Техt Текст, отображаемый на панели
Width Ширина панели
106 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Чтобы добавить панель в область состояния, надо в окне Object Inspector


выбрать свойство Panels, щелчком на кнопке с тремя точками (которая нахо-
дится в области значения свойства) раскрыть окно Editing и в этом окне
щелкнуть на кнопке Add New (рис. 3.29).

Рис. 3.29. Чтобы добавить панель в область состояния,


надо сделать щелчок на кнопке Add New

В качестве примера использования компонента StatusBar на рис. 3.30 при-


ведена форма программы Угадай число. Значения свойств компонента
StatusBar приведены в табл. 3.21, функций обработки событий — в лис-
тинге 3.12.

Рис. 3.30. Форма программы Угадай число

Òàáëèöà. 3.21. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà StatusBar

Свойство Значение
Panels[0].Text Попыток:
Panels[0].Width 70
Panels[1].Text Осталось:
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 107

Листинг 3.12. Угадай число

// Начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
{
// загадать число
Randomize();
pw = RandomRange(100,999); // "секретное" число

// настроить и запустить таймер


Timer1->Interval = 1000;
Timer1->Enabled = true;

// отобразить информацию в панели состояния


StatusBar1->Panels->Items[0]->Text =
" Попыток: 0";
StatusBar1->Panels->Items[1]->Text =
" Осталось: " + IntToStr(rem) + " сек";
}

// Нажатие клавиши в поле редактирования


void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if ( ( Edit1->Text.Length() < 3) && ((Key >= '0') && (Key <= '9')))
return;

if (( Key == VK_RETURN) && (Edit1->Text.Length() == 3))


{
// проверить, правильное ли число ввел пользователь
if ( StrToInt(Edit1->Text) == pw )
{
Timer1->Enabled = false;
Edit1->Enabled = false;

ShowMessage("Поздравляю!\nВы угадали число за " +


IntToStr(TR — rem)+ " сек");
}
else
{
// увеличить счетчик попыток
p++;
StatusBar1->Panels->Items[0]->Text =
" Попыток: " + IntToStr(p);
}
108 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

return;
}

if ( Key == VK_BACK) return;

// остальные символы запрещены


Key = 0;
}

// сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
rem--;
StatusBar1->Panels->Items[1]->Text =
" Осталось: " + IntToStr(rem) + " сек";
if (rem == 0 )
{
// время, отведенное на решение задачи, истекло
Timer1->Enabled = false;
Edit1->Enabled = false;

ShowMessage(
"К сожалению, Вы не справились с поставленной задачей\n"
"\"Секретное\" число: " + IntToStr(pw) );
}
}

UpDown
Компонент UpDown представляет собой две кнопки, используя которые мож-
но изменить значение переменной-счетчика. Обычно компонент UpDown
применяется в связке с компонентом Edit, что позволяет пользователю вве-
сти значение в поле редактирования обычным образом или изменить содер-
жимое поля редактирования с помощью кнопок компонента UpDown. Значок
компонента находится на вкладке Win32 (рис. 3.31), свойства приведены
в табл. 3.22.

Рис. 3.31. Значок компонента UpDown


Ãëàâà 3. Áàçîâûå êîìïîíåíòû 109

Òàáëèöà 3.22. Ñâîéñòâà êîìïîíåíòà UpDown

Свойство Описание

Position Счетчик. Значение свойства изменяется в результате щелчка


на кнопке Up (увеличивается) или Down (уменьшается). Диапа-
зон изменения определяют свойства Min и Max, величину из-
менения — свойство Increment
Min Нижняя граница диапазона изменения свойства Position
Max Верхняя граница диапазона изменения свойства Position
Increment Величина, на которую изменяется значение свойства Position
в результате щелчка на одной из кнопок компонента
Associate Определяет компонент (например, Edit или Label), исполь-
зуемый в качестве индикатора значения свойства Position.
Если в качестве индикатора используется компонент Edit, то
при изменении содержимого поля редактирования автоматиче-
ски меняется значение свойства Position
Orientation Задает ориентацию кнопок компонента. Кнопки могут быть ори-
ентированы вертикально (udVertical) или горизонтально
(udHorizontal)

Программа Будильник (ее форма показана на рис. 3.32) демонстрирует ис-


пользование компонента UpDown. Значения свойств компонентов приведены в
табл. 3.23, текст программы — в листинге 3.13.

Рис. 3.32. Форма программы Будильник

Основную работу выполняет функция обработки события Timer, которое


с периодом 1 с генерирует таймер. Функция сравнивает текущее время (зна-
чение функции Now) со временем, на которое установлен будильник (значение
переменной AlarmTime). Сравнение выполняет функция CompareTime. Чтобы
110 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

эта функция, а также функции HourOf и MinuteOf были доступны, в дирек-


тиву uses надо поместить ссылку на модуль DataUtils.
Воспроизведение звукового сигнала обеспечивает функция PlaySound. В ка-
честве ее первого параметра указан стандартный звуковой сигнал (файлы, в
которых находятся звуки, используемые операционной системой, размеща-
ются в каталоге c:\Windows\Media).

Òàáëèöà 3.23. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


UpDown1 Min 0
Max 23
Associate Edit1
UpDown2 Min 0
Max 59
Associate Edit2

Листинг 3.13. Будильник

#include <dateutils.hpp> // для доступа к HourOf, MinuteOf, CompareTime


#include <mmsystem.hpp> // для доступа к PlaySound

TDateTime AlarmTime; // время сигнала

void __fastcall TForm1::FormCreate(TObject *Sender)


{
// для доступа к MinuteOf и HourOf
// в программу надо добавить директиву #include <DateUtils.hpp>
UpDown1->Position = HourOf(Now());
UpDown2->Position = MinuteOf(Now());
}

// сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{

/* функция CompareTime позволяет сравнить два значения типа TTime:


A B CompareTime(A,B)
13:40 14:40 -1
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 111

14:40 14:40 0
15:40 14:40 1
*/

if ( CompareTime(Now(),AlarmTime) >= 0)
{
Timer1->Enabled = false;
if ( CheckBox1->Checked)
PlaySound("notify.wav",0,SND_ASYNC);

ShowMessage(FormatDateTime(" hh:nn — ", Now() ) + Edit3->Text);

Form1->Show(); // отобразить (развернуть) окно


TrayIcon1->Visible = false;
}
}

// щелчок на кнопке OK
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int h,m; // час, минуты

h = StrToInt(Edit1->Text);
m = StrToInt(Edit2->Text);

/* вручную пользователь может ввести в поля


редактирования неверные данные */
if ( (h > 24) || ( m > 59) ) {
ShowMessage("Неверно задано время сигнала!");
return;
}

Timer1->Enabled = false;
AlarmTime = EncodeTime(h,m,0,0);

Timer1->Enabled = true; // пуск таймера


Form1->Hide(); // свернуть окно программы

TrayIcon1->Hint = FormatDateTime("Будильник — hh:nn", AlarmTime);


TrayIcon1->Visible = true;
}
112 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

// нажатие клавиши в поле редактирования "Часов"


void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{

if ( (isdigit(Key) && Edit1->Text.Length() < 2 )


|| (Key == VK_BACK) )
{
return;
}
Key = 0;
}

// нажатие клавиши в поле редактирования "Минут"


void __fastcall TForm1::Edit2KeyPress(TObject *Sender, char &Key)
{
if ( (isdigit(Key) && Edit2->Text.Length() < 2)
|| (Key == VK_BACK) )
{
return;
}

Key = 0;

// Контекстное меню: команда Открыть


void __fastcall TForm1::N1Click(TObject *Sender)
{
Form1->Show();
TrayIcon1->Visible = false;
}

// Контекстное меню: команда Выключить


void __fastcall TForm1::N2Click(TObject *Sender)
{
Form1->Close();
}

TrayIcon
Компонент TrayIcon представляет собой значок, который отображается в
системной части панели задач во время работы программы, окно которой
свернуто.
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 113

Значок компонента находится на вкладке Additional (рис. 3.33), свойства


приведены в табл. 3.24. Обычно в результате щелчка на значке (правой кноп-
кой мыши) появляется меню (рис. 3.34), его команды используются для
управления работой программы.

Рис. 3.33. Значок компонента TrayIcon

Рис. 3.34. При позиционировании указателя мыши на значке появляется подсказка,


а в результате щелчка правой кнопкой мыши — меню команд

Òàáëèöà 3.24. Ñâîéñòâà êîìïîíåíòà TrayIcon

Свойство Описание

Icon Значок, который отображается в системной части панели задач


Hint Текст подсказки, которая появляется при позиционировании указателя
мыши на значке. Если значение свойства не задано, то отображается
название программы (поле Title вкладки Application окна Project
Options)
Visible Если значение свойства равно true, то значок отображается в панели
задач, в противном случае не отображается
PopupMenu Меню, которое появляется рядом со значком в результате щелчка на
значке правой кнопкой мыши

В качестве примера в табл. 3.25 представлены значения свойств компонентов


TrayIcon и PopupMenu программы Будильник (рис. 3.35), а в листинге 3.14
приведены функции обработки события Click на кнопке OK (эта кнопка за-
пускает будильник) и команд всплывающего меню компонента TrayIcon.
Функция Button1Click запускает таймер, сворачивает окно программы и вы-
полняет настройку компонента TrayIcon.
114 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 3.35. Компоненты TrayIcon и PopupMenu на форме программы Будильник

Òàáëèöà 3.25. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


TrayIcon Icon
Hint Будильник
Visible false
PopupMenu PopupMenu1
PopupMenu1 Items[0].Name N1
Items[0].Caption Открыть
Items[1].Name N2
Items[1].Caption Выключить (завершить работу)

Листинг 3.14. Фрагмент программы Будильник

// щелчок на кнопке OK
void __fastcall TForm1::Button1Click(TObject *Sender)
{
int h,m; // час, минуты

h = StrToInt(Edit1->Text);
m = StrToInt(Edit2->Text);

AlarmTime = EncodeTime(h,m,0,0);

Timer1->Enabled = true; // пуск таймера


Form1->Hide(); // свернуть окно программы
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 115

TrayIcon1->Hint = FormatDateTime("Будильник — hh:nn", AlarmTime);


TrayIcon1->Visible = true;
}
// Контекстное меню: команда Открыть
void __fastcall TForm1::N1Click(TObject *Sender)
{
Form1->Show(); // отобразить окно программы
TrayIcon1->Visible = false;
}

// Контекстное меню: команда Выключить


void __fastcall TForm1::N2Click(TObject *Sender)
{
Form1->Close(); // закрыть окно (завершить работу программы)
}

ProgressBar
Компонент ProgressBar представляет собой индикатор, который обычно ис-
пользуется для наглядного представления протекания процесса (например,
обработки файлов или загрузки информации из сети). Значок компонента на-
ходится на вкладке Win32 (рис. 3.36), свойства приведены в табл. 3.26.

Рис. 3.36. Значок компонента ProgressBar

Òàáëèöà 3.26. Ñâîéñòâà êîìïîíåíòà ProgressBar

Свойство Описание
Position Значение, отображаемое в поле компонента в виде прямоугольника,
ширина которого пропорциональна значению свойства Position
Min Нижняя граница диапазона допустимого значения свойства Position
Max Верхняя граница диапазона допустимого значения свойства Position
Step Шаг изменения значения свойства Position, если для изменения
значения свойства Position используется метод StepIt
Smooth Определяет вид индикатора. Если значение свойства равно false, то
полоса делится на сегменты
116 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Следующая программа (ее форма приведена на рис. 3.37) демонстрирует ис-


пользование компонента ProgressBar. Программа переименовывает файлы
иллюстраций, которые находятся в каталоге, указанном пользователем. Ката-
лог выбирается в стандартном окне Обзор папок (рис. 3.38), появляющемся
на экране в результате щелчка на кнопке Button1. Новое имя файла образует-
ся путем объединения шаблона (текста, введенного в поле Маска) и поряд-
кового номера файла.
Компонент ProgressBar используется в качестве индикатора количества об-
работанных (переименованных) файлов. Настройку компонента ProgressBar
выполняет функция обработки события Click на кнопке Выполнить. Она
устанавливает значение свойства Max равным количеству файлов иллюстра-
ций в обрабатываемом каталоге. Модуль формы программы приведен в лис-
тинге 3.15.

Рис. 3.37. Форма программы Переименовать файлы

Рис. 3.38. Диалог Обзор папок


Ãëàâà 3. Áàçîâûå êîìïîíåíòû 117

Листинг 3.15. Переименовать файлы

// Щелчок на кнопке Выбор папки --------------------------------------


void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString dir; // каталог, выбранный пользователем

if ( SelectDirectory("Выберите каталог", "", dir)) {


// пользователь закрыл диалог щелчком на кнопке OK
Edit1->Text = dir;
}
}

// Щелчок на кнопке Выполнить ----------------------------------------


void __fastcall TForm1::Button2Click(TObject *Sender)
{
AnsiString dir; // каталог
TSearchRec sr; // информация о файле или каталоге
int n = 0; // кол-во файлов в каталоге
AnsiString oldName; // текущее имя файла
AnsiString newName; // новое имя файла
int i = 0; // номер обрабатываемого файла

Label3->Caption = "";
Button1->Enabled = false;

dir = Edit1->Text;
ChDir(dir); // сделать каталог, выбранный пользователем, текущим

// определим, сколько файлов в каталоге


if ( FindFirst("*.jpg", faAnyFile, sr ) == 0 )
do
n++;
while ( FindNext (sr) == 0);
FindClose(sr);

ProgressBar1->Max = n;
ProgressBar1->Position = 0;

// обработка каталога — переименовать файлы


if ( FindFirst("*.jpg", faAnyFile, sr ) == 0 )
do
{
118 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

// чтобы повторно не переименовывать


// уже переименованный файл, проверим имя
// найденного файла. Оно не должно начинаться
// с текста, введенного в поле Edit2
if ( (sr.Name.Pos(Edit2->Text) == 0) ||
(sr.Name.Pos(Edit2->Text) > 1) )
{
i++;
oldName = sr.Name;
Label3->Caption = oldName;

newName = Edit2->Text + IntToStr(i) + ".jpg";

RenameFile(oldName, newName);
ProgressBar1->Position++;
}
}
while ( FindNext (sr) == 0);

FindClose(sr); // освободить память

Label3->Caption = "Обработано файлов: " + IntToStr(n);

Button1->Enabled = true;
}

Image
Компонент Image обеспечивает отображение графики (иллюстраций, фото-
графий, рисунков). Значок компонента находится на вкладке Additional
(рис. 3.39), свойства приведены в табл. 3.27.
Картинку, отображаемую в поле компонента Image, можно задать как во
время разработки формы, так и загрузить из файла во время работы програм-
мы. Если картинка задана во время разработки формы, то файл, из которого
она была загружена, во время работы программы не нужен (копия картинки
помещается в файл ресурса программы). Загрузку картинки из файла во вре-
мя работы программы обеспечивает метод LoadFromFile свойства Picture.

Рис. 3.39. Значок компонента Image


Ãëàâà 3. Áàçîâûå êîìïîíåíòû 119

Необходимо отметить: для того чтобы во время работы программы в поле


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

Òàáëèöà 3.27. Ñâîéñòâà êîìïîíåíòà Image

Свойство Описание
Picture Иллюстрация, которая отображается в поле компонента
Width, Height Размер компонента. Если размер компонента меньше разме-
ра иллюстрации, а значение свойств AutoSize, Strech и
Proportional равно false, то отображается только часть
иллюстрации
AutoSize Признак автоматического изменения размера компонента в
соответствии с реальным размером иллюстрации
Strech Признак автоматического масштабирования (сжатия или рас-
тяжения) иллюстрации в соответствии с реальным размером
компонента. Если размер компонента не пропорционален
размеру иллюстрации, то иллюстрация будет искажена
Proportional Признак автоматического масштабирования картинки без ис-
кажения. Чтобы масштабирование было выполнено, значение
данного свойства должно быть true, а свойства AutoSize —
false
Center Признак определяет расположение картинки в поле компонен-
та по горизонтали, если ширина картинки меньше ширины
поля компонента. Если значение свойства равно true, то кар-
тинка располагается в центре поля компонента
Align Задает границу формы, к которой "привязан" компонент. Если
значение свойства равно alClient, то размер компонента
устанавливается равным размеру "клиентской" (внутренней)
области формы, причем если во время работы программы
будет изменен размер формы, то автоматически будет изме-
нен и размер компонента
AlignWithMargins Если значение свойства равно true, то границы компонента
отодвигаются от границ компонента-контейнера (формы). Ве-
личины отступов задает свойство Margins
Margins Задает величины отступов от границ компонента-контейнера
(формы)
Canvas Поверхность компонента

Следующая программа (ее форма приведена на рис. 3.40) демонстрирует ис-


пользование компонента Image для отображения фотографий. Необходимо
120 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

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


Panel (присвоить свойству Align значение alBottom), а затем компонент Image
(табл. 3.28).
В листинге 3.16 приведены функции обработки событий Click на кнопках
SpeedButton. Функция обработки события Click на SpeedButton1 отображает
стандартное окно Обзор папок и в случае выбора пользователем папки фор-
мирует список иллюстраций, находящихся в выбранном каталоге. Список
(в программе он представлен объектом TstringList) необходим для того, что-
бы обеспечить возможность перехода к предыдущей иллюстрации.

Рис. 3.40. Форма программы Просмотр иллюстраций

Òàáëèöà 3.28. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà Image

Свойство Значение
Align alClient
AlignWithMargins true
Margins.Bottom 3
Margins.Left 3
Margins.Right 3
Margins.Top 3
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 121

Òàáëèöà 3.28 (îêîí÷àíèå)

Свойство Значение
AutoSize false
Proportional true
Center true

Листинг 3.16. Просмотр иллюстраций

#include <jpeg.hpp>
#include <filectrl.hpp>

TStringList *Pictures = new TStringList; // список jpg-файлов


AnsiString aPath; // путь к jpg-файлам
int n; // номер отображаемой иллюстрации

__fastcall TForm1::TForm1(TComponent* Owner)


: TForm(Owner)
{
TSearchRec sr;

SpeedButton2->Enabled = false;
SpeedButton3->Enabled = false;

aPath = Application->GetNamePath();
if ( FindFirst(aPath+ "*.jpg",faAnyFile, sr) == 0 )
{
Image1->Picture->LoadFromFile(sr.Name);
}
}

// щелчок на кнопке Выбор папки


void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
int r;
TSearchRec aSearchRec; // результат поиска файла

if ( SelectDirectory("Выберите каталог, в котором находятся


иллюстрации", "",aPath) )
{
aPath = aPath + "\\";
Form1->Caption = "Просмотр иллюстраций — " + aPath;
122 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

// сформировать список иллюстраций


r = FindFirst(aPath+ "*.jpg",faAnyFile,aSearchRec);
if ( r == 0 )
{
// в указанном каталоге есть jpg-файл
Pictures->Clear(); // очистить список иллюстраций
Pictures->Add(aSearchRec.Name); // добавить имя файла
// в список иллюстраций
}

// получить имена остальных jpg-файлов


do
{
r = FindNext(aSearchRec); // имя следующего jpg-файла
if (r == 0 )
Pictures->Add(aSearchRec.Name);
}
while ( r == 0);

if (Pictures->Count > 1 )
SpeedButton2->Enabled = true;

// отобразить иллюстрацию
n = 0; // номер отображаемой иллюстрации
try
{
Image1->Picture->LoadFromFile(aPath + Pictures->Strings[n]);
}
catch ( EInvalidGraphic &e)
{
Form1->Image1->Picture->Graphic = NULL;
return;
}

Form1->Caption = "Просмотр иллюстраций — " +


aPath + Pictures->Strings[n];

if ( Pictures->Count == 1)
SpeedButton2->Enabled = false;
}
}
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 123

// вывод следующей картинки


void __fastcall TForm1::SpeedButton2Click(TObject *Sender)
{
// вывести картинку
n++;
try
{
Image1->Picture->LoadFromFile(aPath + Pictures->Strings[n]);
}
catch ( EInvalidGraphic &e)
{
Form1->Image1->Picture->Graphic = NULL;
return;
}

Form1->Caption = "Просмотр иллюстраций — " +


aPath + Pictures->Strings[n];

if ( n == (Pictures->Count -1) )
SpeedButton2->Enabled = false;

// если кнопка "Предыдущая" недоступна,


// сделать ее доступной
if ( (n > 0 ) && ( SpeedButton3->Enabled == false))
SpeedButton3->Enabled = true;
}

// вывод предыдущей картинки


void __fastcall TForm1::SpeedButton3Click(TObject *Sender)
{
// вывести картинку
n--;
try
{
Image1->Picture->LoadFromFile(aPath + Pictures->Strings[n]);
}
catch ( EInvalidGraphic &e)
{
Form1->Image1->Picture->Graphic = NULL;
return;
}

Form1->Caption = "Просмотр иллюстраций — " +


aPath + Pictures->Strings[n];
124 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

if ( n == 0 )
SpeedButton3->Enabled = false;

// если кнопка "Следующая" недоступна,


// сделать ее доступной
if ( (n < Pictures->Count) && (SpeedButton2->Enabled == false) )
SpeedButton2->Enabled = true;
}

// Изменилась ширина панели


// (т. к. панель привязана к нижней границе формы,
// то при изменении ширины окна меняется и ширина панели)
void __fastcall TForm1::Panel1Resize(TObject *Sender)
{
// разместить кнопки в центре панели
SpeedButton2->Left = Panel1->Width / 2;
SpeedButton3->Left = Panel1->Width / 2 — SpeedButton3->Width;
}

MainMenu
Компонент MainMenu представляет собой главное меню, его значок находится
на вкладке Standard (рис. 3.41).

Рис. 3.41. Значок компонента MainMenu

После того как компонент MainMenu будет помещен на форму, необходимо


определить структуру меню — ввести название пунктов (например: Файл,
Редактирование, Справка) и команд для каждого пункта (например, для
пункта меню Файл: Новый, Открыть, Сохранить и Выход). Чтобы это сде-
лать, нужно двойным щелчком на значке компонента раскрыть окно редакто-
ра меню.
В начале в окне редактора меню находится один-единственный пустой пря-
моугольник, который изображает новый элемент меню.
Чтобы добавить в меню новый пункт, необходимо в строке меню выбрать
пустой прямоугольник, в окне Object Inspector (в поле значения свойства
Caption) ввести название меню (например, Файл) и нажать клавишу <Enter>.
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 125

В результате в меню будет добавлен новый элемент (создан объект типа


TMenuItem), и в окне редактора меню снизу и справа от только что созданного
элемента появятся два прямоугольника.
Чтобы добавить в созданное меню команду, надо выбрать прямоугольник,
который находится снизу, и в окне Object Inspector (в поле значения свойст-
ва Caption) ввести название команды (например, Новый).
Чтобы добавить в меню раздел, надо выбрать тот прямоугольник, который
находится справа, и в поле значения свойства Caption ввести название раз-
дела меню (например, Справка).
В качестве примера на рис. 3.42 приведено окно редактора меню, в котором
отображается меню программы MEdit. Следует обратить внимание, что по
умолчанию редактор меню присваивает каждому созданному элементу меню
имя, которое состоит из буквы N и порядкового номера элемента. Так первый
элемент меню получает имя N1, второй — N2 и т. д.

Рис. 3.42. Окно редактора меню (настройка компонента MainMenu)

После того как структура меню будет определена, нужно выполнить его на-
стройку.
Каждый элемент меню представляет собой объект типа TMenuItem (свойства
объекта приведены в табл. 3.29).

Òàáëèöà 3.29. Ñâîéñòâà îáúåêòà TMenuItem

Свойство Описание
Name Идентификатор элемента меню
Caption Название элемента меню или команды
126 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Òàáëèöà 3.29 (îêîí÷àíèå)

Свойство Описание

Bitmap Картинка, которая отображается слева от названия элемента меню


Enabled Признак доступности элемента меню (true — элемент доступен,
false — недоступен)
ShortCut Функциональная клавиша или комбинация клавиш (например
<Ctrl>+<z>), с помощью которой можно быстро выбрать элемент меню

Чтобы определить действие, которое должно быть выполнено в результате


выбора команды меню, надо для каждого элемента меню создать функцию
обработки события Click.
Следующая программа, представляющая собой простой, но полнофункцио-
нальный редактор текста, демонстрирует использование компонента
MainMenu. Форма программы приведена на рис. 3.43, значения свойств ком-
понента MainMenu — в табл. 3.30, функции обработки событий — в листин-
ге 3.17.

Рис. 3.43. Компонент MainMenu — главное меню программы MEdit

Òàáëèöà 3.30. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà MainMenu

Компонент (объект) Свойство Значение


MainMenu.Items[1] Caption Файл
Name N1
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 127

Òàáëèöà 3.30 (îêîí÷àíèå)

Компонент (объект) Свойство Значение

MainMenu.Items[2] Caption Новый


Name N2
Bitmap

MainMenu.Items[3] Caption Открыть ...


Name N3
Bitmap

MainMenu.Items[4] Caption Сохранить как ...


Name N4
Bitmap

MainMenu.Items[5] Caption Выход


Name N5
MainMenu.Items[6] Caption Справка
Name N6
MainMenu.Items[7] Caption О программе
Name N7
Enabled false

Листинг 3.17. Редактор текста MEdit

AnsiString fn = ""; // имя редактируемого файла

// Конструктор
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Form1->Width = 640;
Form1->Height = 480;
Form1->Caption = "MEdit — новый документ"
}

// команда Сохранить
void __fastcall TForm1::N4Click(TObject *Sender)
128 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

{
if ( fn != "") {
// редактируется загруженный файл
Memo1->Lines->SaveToFile(fn);
Memo1->Modified = false;
}
else
// имя файла не задано, отобразить SaveDialog
if ( SaveDialog1->Execute() )
{
// пользователь задал имя файла
fn = SaveDialog1->FileName;
Memo1->Lines->SaveToFile( fn );
Memo1->Modified = false;
Form1->Caption = "MEdit — " + fn;
}
}

// команда Выход
void __fastcall TForm1::N5Click(TObject *Sender)
{
Form1->Close();
}

// обработка события CloseQuery (можно закрыть окно?)


void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
AnsiString msg;
int r; // идентификатор кнопки, нажатой пользователем
// в окне MessageDlg

if ( Memo1->Modified)
{

if (fn == "")
msg = "Сохранить набранный текст в файле?";
else
msg = "Текст изменен. Сохранить изменения?";

r = MessageDlg( msg, mtWarning,


TMsgDlgButtons() << mbYes << mbNo << mbCancel, 0);
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 129

switch (r) {

case mrYes:
// записать текст в файл
if ( fn != "")
// имя файла задано
Memo1->Lines->SaveToFile(fn );
else
// имя файла не задано, отобразить SaveDialog
if ( SaveDialog1->Execute() )
// пользователь задал имя файла
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
else
// пользователь нажал кнопку Отменить,
// продолжить работу с текстом
CanClose = false;
break;
case mrCancel:
// отказ от завершения работы
CanClose = false;
break;
}
}
}

// команда Открыть
void __fastcall TForm1::N3Click(TObject *Sender)
{
AnsiString msg;
int r; // идентификатор кнопки, нажатой пользователем
// в окне MessageDlg

bool go = true; // true — открыть; false — отмена действия

if ( Memo1->Modified)
{
if (fn == "")
msg = "Сохранить набранный текст в файле?";
else
msg = "Текст изменен. Сохранить изменения?";

r = MessageDlg( msg, mtWarning,


TMsgDlgButtons() << mbYes << mbNo << mbCancel, 0);
130 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

switch (r) {

case mrYes: // записать текст в файл


if ( fn != "") {
// имя файла задано
Memo1->Lines->SaveToFile(fn );
Memo1->Modified = false;
}
else
// имя файла не задано, отобразить SaveDialog
if ( SaveDialog1->Execute() ) {
// пользователь задал имя файла
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
Memo1->Modified = false;
}
else
// пользователь не задал имя файла
// и тем самым отказался от открытия файла
go = false;
break;

case mrCancel: // отказ от открытия файла


go = false;
break;
}
}

// открыть, если пользователь не отказался


if (go) {
if ( OpenDialog1->Execute())
{
fn = OpenDialog1->FileName;
Memo1->Lines->LoadFromFile(fn);
Form1->Caption = "MEdit — " + fn;
}
}
}

// команда Новый
void __fastcall TForm1::N2Click(TObject *Sender)
{
AnsiString msg;
int r; // идентификатор кнопки, нажатой пользователем
// в окне MessageDlg
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 131

bool go = true; // true — новый файл; false — отмена действия

if ( Memo1->Modified)
{
if (fn == "")
msg = "Сохранить набранный текст в файле?";
else
msg = "Текст изменен. Сохранить изменения?";

r = MessageDlg( msg, mtWarning,


TMsgDlgButtons() << mbYes << mbNo << mbCancel, 0);

switch (r) {

case mrYes: // записать текст в файл


if ( fn != "") {
// имя файла задано
Memo1->Lines->SaveToFile(fn );
Memo1->Modified = false;
}
else
// имя файла не задано, отобразить SaveDialog
if ( SaveDialog1->Execute() ) {
// пользователь задал имя файла
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
Memo1->Modified = false;
}
else
// пользователь не задал имя файла
// и тем самым отказался от создания файла
go = false;
break;

case mrCancel: // отказ от создания файла


go = false;
break;
}
}
if (go) {
fn = "";
Memo1->Lines->Clear();
Form1->Caption = "MEdit — новый документ";
}
}
132 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

PopupMenu
Компонент PopupMenu представляет собой меню, которое появляется в ре-
зультате щелчка правой кнопкой мыши в поле компонента, с которым связа-
но меню (такое меню называют всплывающим или контекстным). Значок
компонента находится на вкладке Standard (рис. 3.44).

Рис. 3.44. Значок компонента PopupMenu

Процесс настройки всплывающего меню практически ничем не отличается от


настройки главного меню (компонента MainMenu). Как и элементы главного
меню, элементы всплывающего меню представляют собой объекты типа
TMenuItem. В качестве примера на рис. 3.45 приведено окно редактора меню
во время настройки всплывающего меню (компонента PopupMenu) программы
MEdit. Значения свойств компонента представлены в табл. 3.31, функции
обработки команд — в листинге 3.18. Следует обратить внимание: для того
чтобы всплывающее меню появлялось на экране в результате щелчка правой
кнопкой мыши в поле компонента (например, в поле редактирования), в
свойство PopupMenu этого компонента надо записать имя компонента, пред-
ставляющего собой всплывающее меню.

Рис. 3.45. Настройка всплывающего меню программы MEdit


Ãëàâà 3. Áàçîâûå êîìïîíåíòû 133

Òàáëèöà 3.31. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà PopupMenu1

Компонент (объект) Свойство Значение

PopupMenu1.Items[1] Caption Увеличить шрифт


Name N1

Bitmap

PopupMenu1.Items[2] Caption Уменьшить шрифт


Name N2

Bitmap

Листинг 3.18. Функции обработки команд всплывающего меню

// команда Увеличить шрифт


void __fastcall TForm1::N1Click(TObject *Sender)
{
Memo1->Font->Size = Memo1->Font->Size + 2;
if ( Memo1->Font->Size == 14)
PopupMenu1->Items[1].Enabled = false; // больше увеличить нельзя

if ( Memo1->Font->Size > 8)
PopupMenu1->Items[2].Enabled = true; // можно уменьшить
}

// команда Уменьшить шрифт


void __fastcall TForm1::N2Click(TObject *Sender)
{
Memo1->Font->Size = Memo1->Font->Size — 2;
if (Memo1->Font->Size == 8)
PopupMenu1->Items[2].Enabled = false; // больше уменьшить нельзя

if ( Memo1->Font->Size < 14)


PopupMenu1->Items[1].Enabled = true; // можно увеличить
}

OpenDialog
Компонент OpenDialog представляет собой диалог Открыть. Значок ком-
понента находится на вкладке Standard (рис. 3.46), свойства приведены в
134 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

табл. 3.32. Отображение диалога обеспечивает метод Execute, значение кото-


рого позволяет определить, щелчком на какой кнопке (Открыть или Отме-
на) пользователь закрыл диалог.

Рис. 3.46. Значок компонента OpenDialog

Òàáëèöà 3.32. Ñâîéñòâà êîìïîíåíòà OpenDialog

Свойство Описание
Title Текст в заголовке окна. Если значение свойства не указано, то
в заголовке отображается текст Открыть
Filter Свойство задает список фильтров имен файлов. В списке фай-
лов отображаются только те файлы, имена которых соответст-
вуют выбранному (текущему) фильтру. Во время отображения
диалога пользователь может выбрать фильтр в списке Тип
файлов. Каждый фильтр задается строкой вида: описа-
ние|маска (например, Текст|*.txt)
FilterIndex Если в списке Filter несколько элементов (например, Текст|*.txt
|Все файлы|*.*), то значение свойства задает фильтр, который
используется в момент появления диалога на экране
InitialDir Каталог, содержимое которого отображается при появлении
диалога на экране. Если значение свойства не указано, то в
окне диалога отображается содержимое папки Мои документы
FileNane Имя файла, который выбрал пользователь
Options Параметры, позволяющие выполнить "тонкую" настройку диа-
лога:
OfPathMustExist — устанавливает режим проверки сущест-
вования каталога, указанного перед именем файла;
OfFileMustExist — устанавливает режим проверки сущест-
вования файла, имя которого введено в поле Файл;
OfNoChangeDir — устанавливает (true), что пользователь
может выбрать файл только в каталоге, заданном параметром
InitialDir;
OfEnableSizing — разрешает (true) или запрещает (false)
изменение размера окна диалога
OptionsEx Дополнительные параметры: ofExNoPlacesBar — запрещает
(false) или разрешает (true) отображение в окне диалога
кнопок Мои документы, Сетевое окружение, Рабочий стол
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 135

В качестве примера использования диалога OpenDialog в листинге 3.19 при-


веден фрагмент программы MEdit — функции обработки события, возни-
кающего в результате выбора в меню Файл команды Открыть. Форма про-
граммы и значения свойств компонента OpenDialog приведены, соответст-
венно, на рис. 3.47 и в табл. 3.33.

Рис. 3.47. Компонент OpenDialog обеспечивает отображение диалога Открыть

Òàáëèöà 3.33. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà OpenDialog

Свойство Значение
Title Открыть файл
Filter Текст|*.txt |Все файлы|*.*

Листинг. 3.19. Команда Открыть

// команда Открыть
void __fastcall TForm1::N3Click(TObject *Sender)
{
AnsiString msg;
int r; // идентификатор кнопки, нажатой пользователем
// в окне MessageDlg

bool go = true; // true — открыть; false — отмена действия

if ( Memo1->Modified)
{
136 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

if (fn == "")
msg = "Сохранить набранный текст в файле?";
else
msg = "Текст изменен. Сохранить изменения?";

r = MessageDlg( msg, mtWarning,


TMsgDlgButtons() << mbYes << mbNo << mbCancel, 0);

switch (r) {

case mrYes: // записать текст в файл


if ( fn != "") {
// имя файла задано
Memo1->Lines->SaveToFile(fn );
Memo1->Modified = false;
}
else
// имя файла не задано, отобразить SaveDialog
if ( SaveDialog1->Execute() ) {
// пользователь задал имя файла
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
Memo1->Modified = false;
}
else
// пользователь не задал имя файла
// и тем самым отказался от открытия файла
go = false;
break;

case mrCancel: // отказ от открытия файла


go = false;
break;
}
}

// открыть, если пользователь не отказался


if (go) {
if ( OpenDialog1->Execute())
{
fn = OpenDialog1->FileName;
Memo1->Lines->LoadFromFile(fn);
Form1->Caption = fn;
}
}
}
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 137

SaveDialog
Компонент SaveDialog представляет собой диалог Сохранить. Значок ком-
понента находится на вкладке Dialogs (рис. 3.48), свойства приведены в
табл. 3.34. Отображение диалога обеспечивает метод Execute, значение кото-
рого позволяет определить, щелчком на какой кнопке (Открыть или Отме-
на) пользователь закрыл диалог.

Рис. 3.48. Значок компонента SaveDialog

Òàáëèöà 3.34. Ñâîéñòâà êîìïîíåíòà SaveDialog

Свойство Описание
Title Текст в заголовке окна. Если значение свойства не указано, то в
заголовке отображается текст Сохранить как
Filter Свойство задает список фильтров имен файлов. В списке фай-
лов отображаются только те файлы, имена которых соответст-
вуют выбранному (текущему) фильтру. Во время отображения
диалога пользователь может выбрать фильтр в списке Тип
файлов. Каждый фильтр задается строкой вида: описа-
ние|маска (например, Текст|*.txt)
FilterIndex Если в списке Filter несколько элементов (например, Текст|*.txt
|Все файлы|*.*), то значение свойства задает фильтр, который
используется в момент появления диалога на экране
InitialDir Каталог, содержимое которого отображается при появлении
диалога на экране. Если значение свойства не указано, то в
окне диалога отображается содержимое папки Мои документы
FileNane Имя файла, введенное пользователем в поле Имя файла
DefaultExt Расширение, которое будет добавлено к имени файла, если
в поле Имя файла пользователь не задаст расширение файла

В качестве примера использования компонента SaveDialog в листинге 3.20


приведен фрагмент программы MEdit — функции обработки событий, воз-
никающих в результате выбора в меню Файл команд Сохранить и Выход, а
также функция обработки события CloseQuery формы, которое возникает
при попытке закрыть окно. Форма программы и значения свойств компонен-
та SaveDialog приведены, соответственно, на рис. 3.49 и в табл. 3.35.
138 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

Рис. 3.49. Компонент SaveDialog обеспечивает отображение


диалога Сохранить

Òàáëèöà 3.35. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà SaveDialog

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

DefaultExt txt

Filter Текст|*.txt

Листинг 3.20. Команды Сохранить, Выход

// команда Сохранить
void __fastcall TForm1::N4Click(TObject *Sender)
{
// fn — имя редактируемого файла
if ( fn != "") {
// редактируется загруженный файл
Memo1->Lines->SaveToFile(fn);
Memo1->Modified = false;
}
else
// имя файла не задано, отобразить SaveDialog
if ( SaveDialog1->Execute() )
{
// пользователь задал имя файла
fn = SaveDialog1->FileName;
Ãëàâà 3. Áàçîâûå êîìïîíåíòû 139

Memo1->Lines->SaveToFile( fn );
Memo1->Modified = false;
Form1->Caption = fn;
}
}

// команда Выход
void __fastcall TForm1::N5Click(TObject *Sender)
{
Form1->Close();
}

// обработка события CloseQuery (можно закрыть окно?)


void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
AnsiString msg;
int r; // идентификатор кнопки, нажатой пользователем
// в окне MessageDlg

if ( Memo1->Modified)
{
if (fn == "")
msg = "Сохранить набранный текст в файле?";
else
msg = "Текст изменен. Сохранить изменения?";

r = MessageDlg( msg, mtWarning,


TMsgDlgButtons() << mbYes << mbNo << mbCancel, 0);

switch (r) {

case mrYes:
// записать текст в файл
if ( fn != "")
// имя файла задано
Memo1->Lines->SaveToFile(fn );
else
// имя файла не задано, отобразить SaveDialog
if ( SaveDialog1->Execute() )
// пользователь задал имя файла
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
140 ×àñòü I. Ñðåäà ðàçðàáîòêè C++ Builder

else
// пользователь не задал файла,
// продолжить работу с текстом
CanClose = false;
break;
case mrCancel:
// отказ от завершения работы
CanClose = false;
break;
}
}
}
часть II

Ïðàêòèêóì
ïðîãðàììèðîâàíèÿ
Глава 4. Графика
Глава 5. Мультимедиа
Глава 6. Базы данных
Глава 7. Компонент программиста
Глава 8. Справочная информация
Глава 9. Создание установочного диска
Глава 10. Примеры программ
ÃËÀÂÀ 4

Ãðàôèêà

В этой главе рассказывается о том, что надо сделать, чтобы на поверхности


формы появилась фотография, иллюстрация или сформированная из графи-
ческих элементов картинка (например, диаграмма). Также вы познакомитесь
с принципами реализации анимации, узнаете, как "оживить" картинку.

Ãðàôè÷åñêàÿ ïîâåðõíîñòü
Программа может вывести графику на поверхность компонента PaintBox или
непосредственно на поверхность формы.
Графика формируется на графической поверхности компонента (или формы).
Доступ к графической поверхности обеспечивает свойство Canvas, представ-
ляющее собой объект TCanvas, методы (табл. 4.1) и свойства (табл. 4.2) кото-
рого, в свою очередь, обеспечивают формирование графики.

Òàáëèöà 4.1. Ìåòîäû êëàññà TCanvas

Метод Действие
LineTo(x,y) Рисует линию из текущей точки в точку с ука-
занными координатами. Вид линии определяет
свойство Pen
MoveTo(x,y) Перемещает указатель текущей точки (перо)
в точку с указанными координатами
Rectangle(x1,y1,x2,y2) Рисует прямоугольник. Параметры x1, y1 и x2,
y2 задают координаты находящихся на одной
диагонали углов прямоугольника. Цвет границы
прямоугольника определяет свойство Pen, цвет
закраски области — свойство Brush
Ãëàâà 4. Ãðàôèêà 143

Òàáëèöà 4.1 (ïðîäîëæåíèå)

Метод Действие

RoundRect(x1,y1,x2,y2,x3,y3) Рисует прямоугольник со скругленными углами.


Параметры x1, y1 и x2, y2 задают координаты
находящихся на одной диагонали углов прямо-
угольника, параметры x3, y3 — радиус скругле-
ния. Цвет границы прямоугольника определяет
свойство Pen, цвет закраски области — свойст-
во Brush

Ellipse(x1,y1, x2,y2) Рисует эллипс (окружность). Параметры x1, y1,


x2, y2 задают координаты углов прямоугольни-
ка, внутри которого вычерчивается эллипс (ок-
ружность, если прямоугольник является квадра-
том). Цвет границы определяет свойство Pen,
цвет закраски области — свойство Brush

Arc(x1,y1,x2,y2,x3,y3,x4,y4) Рисует дугу. Параметры x1, y1, x2 и y2 задают


эллипс, частью которого является дуга, пара-
метры x3, y3, x4 и y4 — начальную и конечную
точку дуги. Цвет дуги определяет свойство Pen

Pie(x1,y1,x2,y2,x3,y3,x4,y4) Рисует сектор. Параметры x1, y1, x2 и y2 за-


дают эллипс, частью которого является сектор,
параметры x3, y3, x4 и y4 — границы сектора.
Цвет границы сектора определяет свойство Pen,
цвет закраски сектора — свойство Brush

FillRect(aRect) Рисует закрашенный прямоугольник. Параметр


aRect (тип TRect) определяет положение и
размер прямоугольника. Цвет закраски области
определяет свойство Brush

FrameRect(aRect) Рисует контур прямоугольника. Параметр aRect


(тип TRect) определяет положение и размер
прямоугольника. Цвет контура определяет свой-
ство Brush

Polyline(points,n) Рисует ломаную линию. Points — массив типа


TPoint. Каждый элемент массива представляет
собой запись, поля x и y которой содержат ко-
ординаты точки перегиба ломаной, n — количе-
ство звеньев ломаной. Метод Polyline вычер-
чивает ломаную линию, последовательно со-
единяя прямыми точки, координаты которых
находятся в массиве: первую со второй, вторую
с третьей, третью с четвертой и т. д.
144 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 4.1 (îêîí÷àíèå)

Метод Действие

Draw(x,y,bm) Выводит на графическую поверхность картинку.


Параметры x и y задают положение области
отображения картинки, параметр bm (объект
TBitmap) — саму картинку

CopyRect(rec1, Canvas2, Копирует фрагмент рисунка с одной графиче-


rec2) ской поверхности на другую. Параметр Canvas2
задает поверхность, откуда копируется картин-
ка. Параметры rect1 и rect2 задают, соот-
ветственно, положение и размер области, куда
выполняется копирование. Объект, к которому
применяется метод, является поверхностью, на
которую копируется фрагмент картинки с по-
верхности Canvas2

Òàáëèöà 4.2. Ñâîéñòâà êëàññà TCanvas

Свойство Описание

Pen Перо. Определяет цвет, толщину и стиль линии

Brush Кисть. Определяет цвет и стиль закраски области

Font Шрифт. Определяет шрифт, используемый для вывода


текста

Положение точки на графической поверхности характеризуется ее горизон-


тальной (x) и вертикальной (y) координатами. Координаты возрастают слева
направо и сверху вниз (рис. 4.1). Левая верхняя точка имеет координаты
(0, 0).
В программе координаты точки графической поверхности можно хранить в
двух независимых переменных или в структуре TPoint (точка). У структуры
TPoint два поля: x и y. Поле х соответствует горизонтальной координате
точки, y — вертикальной. При программировании графики также весьма по-
лезна структура (класс) TRect (прямоугольник). Структура представляет со-
бой объединение четырех переменных целого типа (top, left, right, bottom),
что позволяет хранить координаты двух точек графической поверхности, т. е.
информацию о положении и размере прямоугольной области.
Ãëàâà 4. Ãðàôèêà 145

Рис. 4.1. Координаты точек графической поверхности

Âûâîä ãðàôèêè
Графику на поверхности компонента PaintBox или формы должна формиро-
вать функция обработки события Paint.
Событие Paint возникает в начале работы программы, когда окно первый раз
появляется на экране, а также всякий раз, когда необходимо снова нарисовать
окно (например, после того как пользователь сдвинет другое окно, частично
или полностью перекрывающее окно программы, или развернет свернутое
окно).
Функция обработки события Paint создается обычным образом. Если графи-
ка отображается в поле компонента PaintBox, то надо создать функцию обра-
ботки события для компонента, если на поверхности формы — то для формы.

Ïåðî è êèñòü
Художник в своей работе использует карандаши (перья) и кисти. Каранда-
шами он рисует линии, а кистями закрашивает области. Точно так же и мето-
ды, рисующие на графической поверхности, используют перья и кисти.
Вид графического элемента, нарисованного соответствующим методом, оп-
ределяют свойства Pen (перо) и Brush (кисть) той поверхности (Canvas), на
которой метод рисует.
Перо и кисть, являясь свойствами объекта Canvas, представляют собой объек-
ты Pen и Brush. Свойства объекта Pen (табл. 4.3) определяют вид линии (цвет,
146 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

толщина, стиль), а свойства объекта Brush (табл. 4.4) — вид области (цвет и
стиль закраски).

Òàáëèöà 4.3. Ñâîéñòâà îáúåêòà Pen (ïåðî)

Свойство Описание
Color Цвет линии
Width Толщина линии
Style Вид линии:
psSolid — сплошная;
psDash — пунктирная, длинные штрихи;
psDot — пунктирная, короткие штрихи;
psDashDot — пунктирная, чередование длинного и короткого штрихов;
psDashDotDot — пунктирная, чередование одного длинного и двух
коротких штрихов;
psClear — линия не отображается (используется, если не надо изо-
бражать границу области, например, прямоугольника)
PenPos Текущее положение пера. Свойство представляет собой структуру
типа TPoint, поля x и y которой содержат, соответственно, горизон-
тальную и вертикальную координаты точки графической поверхности,
в которой находится перо

Òàáëèöà 4.4. Ñâîéñòâà îáúåêòà Brush (êèñòü)

Свойство Описание
Color Цвет закраски области
Style Стиль закраски области:
bsSolid — сплошная заливка;
bsHorizontal — горизонтальная штриховка;
bsVertical — вертикальная штриховка;
bsFDiagonal — диагональная штриховка с наклоном линий вперед;
bsBDiagonal — диагональная штриховка с наклоном линий назад;
bsCross — клетка;
bsDiagCross — диагональная клетка
Ãëàâà 4. Ãðàôèêà 147

Цвет линии и закраски области задается путем присваивания значения свой-


ству Pen.Color или Brush.Color. В табл. 4.5 приведены константы, которые
можно использовать, чтобы задать цвет линии или закраски области.

Òàáëèöà 4.5. Êîíñòàíòû TColor

Цвет Константа
clAqua Бирюзовый
clBlack Черный
clBlue Синий
clFuchsia Ярко-розовый
clGreen Зеленый
clLime Салатный
clMaroon Каштановый
clNavy Темно-синий
clOlive Оливковый
clPurple Розовый
clRed Красный
clSilver Серебристый
clTeal Зелено-голубой
clWhite Белый

Если необходимо установить цвет, отличный от стандартного, то в качестве


значения свойства Color надо указать RGB-код этого цвета (любой цвет
можно получить путем смешивания в разных пропорциях красной (Red),
зеленой (Green) и синей (Blue) красок). Получить код цвета можно, об-
ратившись к функции (макросу) RGB, указав в качестве параметров долю
красной, зеленой и синей составляющей. Например, значением
RGB(56,176,222) является код цвета "осеннее небо". Следует обратить внима-
ние: чтобы макрос RGB стал доступен, в текст программы надо включить ди-
рективу #include <wingdi.h>.

Ãðàôè÷åñêèå ïðèìèòèâû
Картинку, график или схему можно рассматривать как совокупность графи-
ческих примитивов: точек, линий, окружностей, прямоугольников, дуг, а
также текста.
148 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Вычерчивание графических примитивов (элементов) на графической поверх-


ности выполняют соответствующие методы класса TCanvas (см. тaбл. 4.1).
Инструкция, обеспечивающая вычерчивание графического элемента, в общем
виде выглядит так:
Объект->Canvas->Mетод(Параметры);

Объектопределяет объект, на поверхности которого нужно нарисовать графи-


ческий элемент. В качестве объекта обычно указывается компонент PaintBox.
Метод — это имя метода, который обеспечивает рисование нужного графиче-
ского элемента. Параметры определяют положение графического элемента на
графической поверхности и его размер.
Например, в результате выполнения инструкции
PaintBox1->Canvas->Rectangle(10,20,60,40);

на поверхности компонента PaintBox1 будет нарисован прямоугольник ши-


риной 50 и высотой 20 пикселов, левый верхний угол которого будет нахо-
диться в точке (10, 20).

Òåêñò
Вывод текста на графическую поверхность объекта обеспечивает метод
TextOut. Инструкция вызова метода TextOut в общем виде выглядит следую-
щим образом:
Объект->Canvas->TextOut(x, y, Текст);

Параметры x и y определяют координаты точки графической поверхности, от


которой выполняется вывод текста (рис. 4.2). Необходимо обратить внима-
ние, что область вывода текста закрашивается текущим цветом кисти. Кроме
того, после вывода текста карандаш (Pen) автоматически перемещается в точ-
ку, координата которой совпадает с координатой правого верхнего угла об-
ласти вывода текста. Размер области вывода зависит от длины текста (коли-
чества символов) и шрифта, который используется для его отображения.
Шрифт, используемый для отображения текста, определяется свойством Font
графической поверхности. Свойство Font представляет собой объект типа
TFont. В табл. 4.6 перечислены свойства класса TFont.

Òàáëèöà 4.6. Ñâîéñòâà êëàññà TFont

Свойство Определяет
Name Шрифт, который используется для отображения текста. В качестве
значения следует брать название шрифта, например Arial
Ãëàâà 4. Ãðàôèêà 149

Òàáëèöà 4.6 (îêîí÷àíèå)

Свойство Определяет
Size Размер шрифта
Style Стиль начертания символов. Задается с помощью констант:
fsBold (полужирный);
fsItalic (курсив);
fsUnderline (подчеркнутый);
fsStrikeOut (перечеркнутый)
Color Цвет символов. В качестве значения можно использовать константу
типа TColor

Рис. 4.2. Вывод текста на поверхность формы

Следует обратить внимание: свойство Style является множеством, что по-


зволяет комбинировать необходимые стили. Например, инструкция, которая
устанавливает стиль "полужирный курсив", выглядит так:
Canvas->Font.Style = TFontStyles() << fsBold << fsItalic;

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


TextWidth и TextHeight, позволяющие определить размер (соответственно
ширину и высоту) области вывода текста. Обоим этим методам в качестве
параметра передается строка, которую предполагается вывести на графиче-
скую поверхность методом TextOut.
Фрагмент кода (листинг 4.1) демонстрирует использование метода TextOut
для вывода текста на поверхность формы. Приведенная функция обработки
150 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

события Paint закрашивает верхнюю половину окна белым, нижнюю —


желтым цветом, затем в центре окна выводит текст (рис. 4.3).

Рис. 4.3. Вывод текста на графическую поверхность обеспечивает метод TextOut

Листинг 4.1. Вывод текста на поверхность формы

// обработка события Paint


void __fastcall TForm1::FormPaint(TObject *Sender)
{
AnsiString msg;
TRect aRect; // прямоугольник
int x,y;

// верхнюю половину окна красим белым


aRect = Rect(0,0,ClientWidth,ClientHeight/2);
Canvas->Brush->Color = clWhite;
Canvas->FillRect(aRect);

// нижнюю половину окна красим желтым


aRect = Rect(0,ClientHeight/2,ClientWidth,ClientHeight);
Canvas->Brush->Color = RGB(247,209,23);
Canvas->FillRect(aRect);

// характеристики шрифта
Canvas->Font->Name = "Times New Roman";
Canvas->Font->Size = 26;
Canvas->Font->Color = clBlack;

Canvas->Brush->Style = bsClear; // область вывода текста


// не закрашивать
msg = "C++ Builder";

// текст разместим в центре окна


x = (ClientWidth — Canvas->TextWidth(msg)) /2;
y = ClientHeight/2 — Canvas->TextHeight(msg) /2;
Ãëàâà 4. Ãðàôèêà 151

Canvas->TextOut(x,y, msg); // вывести текст

// вычислить координату Y следующей строки


y += Canvas->TextHeight(msg);

msg = "Сегодня " + DateToStr(Now());

Canvas->Font->Size = 12;
x = (ClientWidth — Canvas->TextWidth(msg)) /2;

Canvas->TextOut(x,y, msg); // вывести текст


}

Часто требуется вывести какой-либо текст после сообщения, длина которого


во время разработки программы неизвестна. В этом случае необходимо знать
координаты правой границы области выведенного текста. Координаты пра-
вой границы текста, показанного методом TextOut, можно получить, обра-
тившись к свойству PenPos.
Следующий фрагмент кода демонстрирует возможность вывода строки тек-
ста с помощью двух инструкций TextOut:
PaintBox1->Canvas->TextOut(10, 10, "Borland ");
PaintBox1->Canvas->TextOut(PenPos.x, PenPos.y, "C++ Builder");

Ëèíèÿ
Метод LineTo рисует линию из точки, в которой находится перо, в точку, ко-
ординаты которой указаны в качестве параметров метода.
Инструкция вызова метода в общем виде выглядит так:
Объект->Canvas->LineTo(x,y);

Цвет, стиль и толщину линии определяют, соответственно, свойства:


Pen.Color, Pen.Style и Pen.Width графической поверхности, на которой ме-
тод рисует. Начальную точку линии можно задать, переместив перо в нуж-
ную точку. Сделать это можно с помощью метода MoveTo или присвоив зна-
чение свойству PenPos. Следует обратить внимание, что после того как линия
будет нарисована, перо будет находиться в точке ее конца.
В качестве примера в листинге 4.2 приведена программа, которая строит на
поверхности формы график изменения курса доллара (рис. 4.4). Данные, ото-
бражаемые на графике, считываются из файла в массив kurs (делает это
функция обработки события FormActivate). На графике отображаются дан-
ные за десять последних дней (из файла считываются первые 10 строк).
152 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

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

Рис. 4.4. Пример графика

Листинг 4.2. График

#define HB 10

double kurs[HB]; // курс за последние 10 дней


TDateTime d1,d2; // диапазон дат: с d1 по d2

// читает строку из файла


int FileReadLine(int h, AnsiString *st)
{
int n = 0; // длина прочитанной строки
int r; // кол-во символов, прочитанных FileRead
char *p;
char buf[256];

p = buf;

do {
r = FileRead(h, p, 1); // прочитать один символ
Ãëàâà 4. Ãðàôèêà 153

if ( (r != 0) && ( *p != '\r')) {
n ++;
p++;
}
}
while ( (r != 0) && ( *p != '\r') );

if ( *p == '\r' )
r = FileRead(h, p, 1);

*p = '\0';
st->sprintf("%s",buf);

return(n);
}

// начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
{
AnsiString fn; // файл данных
int f; // дескриптор файла
AnsiString st; // строка, прочитанная из файла

// загрузить данные из файла в массив


fn = "usd.txt";
if ( ! FileExists(fn)) {
MessageDlg("Ошибка доступа к файлу данных",mtWarning,
TMsgDlgButtons() << mbOK,0);
return;
}

f = FileOpen( fn, fmOpenRead); // открыть файл для чтения

int i = HB-1;
while (( ( FileReadLine(f, &st)) != 0) && ( i >= 0 ))
{
// строка, считанная из файла, имеет вид: dd.mm.yy 99.9999
// т. е. первые 8 символов — дата, остальные — курс
if (i == HB-1) d2 = StrToDate(st.SubString(1,8));
kurs[i] = st.SubString(10,7).ToDouble();
i--;
}
154 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

d1 = StrToDate(st.SubString(1,8));
FileClose(f);
}

// построить график
void __fastcall TForm1::FormPaint(TObject *Sender)
{
int x,y; // координаты точки
int dx; // шаг по X

int x0,y0; // координаты левого нижнего угла


// области построения графика

AnsiString st = "Курс доллара";

// заголовок
Canvas->Font->Name = "Arial";
Canvas->Font->Size = 14;
x = (ClientWidth — Canvas->TextWidth(st)) /2;
y = 10;
Canvas->Brush->Style = bsClear;
Canvas->TextOut(x,y,st);

y += Canvas->TextHeight(st);
Canvas->Font->Size = 9;
st = DateToStr(d1) + " — " + DateToStr(d2);
x = (ClientWidth — Canvas->TextWidth(st)) /2;
Canvas->Brush->Style = bsClear;
Canvas->TextOut(x,y, st);

// *** найти минимальное и максимальное значения данных ***


int min,max; // индекс минимального и максимального элемента

min = 0; // пусть первый элемент минимальный


max = 0; // пусть первый элемент максимальный
for (int i = 1; i < HB; i++)
{
if (kurs[i] < kurs[min]) min = i;
if (kurs[i] > kurs[max]) max = i;
}

/* Если отклонения значений ряда от среднего значения


незначительные, то диаграмма получается ненаглядной.
Ãëàâà 4. Ãðàôèêà 155

В этом случае можно построить не абсолютные значения,


а отклонения от минимального значения ряда. */

bool frmin = true; // true — отсчитывать от минимального значения

dx= (ClientWidth — 20) / (HB — 1);

Canvas->Font->Size = 10;
Canvas->Pen->Color = clGreen;
Canvas->Pen->Width = 1;
Canvas->Brush->Style = bsClear;

x0 = 10;
y0 = ClientHeight — 10;

x = x0;
dx= (ClientWidth — 40) / (HB-1);
for ( int i = 0; i < HB; i++ )
{
/* максимальному значению соответствует
точка с координатой ClientHeight — 90 */
if (! frmin)
y = y0 + (ClientHeight — 90) * kurs[i]/kurs[max];
else
// Отсчитывать от минимального значения
y = y0-(ClientHeight — 90) * (kurs[i] — kurs[min])/
(kurs[max] — kurs[min])-10;

// поставить точку
Canvas->Rectangle(x-2,y-2,x+2,y+2);

if (i != 0)
Canvas->LineTo(x,y);

// ** подпись данных **
/* т. к. метод TextOut изменит положение точки привязки (точки,
из которой рисует метод LineTo), то после вывода текста
надо будет переместить указатель в точку (x,y) */

if ( ( i == 0) || (kurs[i] != kurs[i-1]))
{
AnsiString st;
st = FloatToStrF(kurs[i],ffNumber,5,2);
156 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Canvas->TextOut(x,y-20,st);
}
Canvas->MoveTo(x,y);
x += dx;
}
}

// размер окна изменился


void __fastcall TForm1::FormResize(TObject *Sender)
{
// обновить график
Form1->Refresh();
}

Ëîìàíàÿ ëèíèÿ
Метод Polyline чертит ломаную линию. Инструкция вызова метода в общем
виде выглядит так:
Объект->Canvas->Polyline(p,n);

В качестве параметров методу передается массив типа TPoint, элементы ко-


торого содержат координаты узловых точек линии и количество звеньев ло-
маной.
Метод Polyline вычерчивает ломаную линию, последовательно соединяя
точки, координаты которых находятся в массиве: первую со второй, вторую с
третьей, третью с четвертой и т. д. Цвет, стиль и толщину линии определяют,
соответственно, свойства Pen.Color, Pen.Style и Pen.Width той поверхности,
на которой метод чертит.
Следующий фрагмент кода (листинг 4.3) рисует флажок (рис. 4.5), который
можно рассматривать как ломаную, состоящую из трех звеньев (номера точек
ломаной соответствуют индексам элементов массива).

Листинг 4.3. Ломаная линия

void __fastcall TForm1::PaintBox1Paint(TObject *Sender)


{
TPoint p[4]; // координаты точек перегиба
int dx,dy: // шаг сетки

dx = 5; dy =5;
// координаты будем отсчитывать от верхней
// точки древка (от точки 1)
Ãëàâà 4. Ãðàôèêà 157

p[1].x = 10;
p[1].y = 10;
p[0].x = p[1].x;
p[0].y = p[1].y + 8*dy;
p[2].x = p[1].x + 7*dx;
p[2].y = p[1].y + 3*dy;
p[3].x = p[1].x;
p[3].y = p[1].y + 5*dy;

PaintBox1->Canvas->Polyline(p,3);
}

Рис. 4.5. Флажок — пример ломаной линии

В приведенном примере базовой точкой является верхняя точка древка (от


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

Ïðÿìîóãîëüíèê
Метод Rectangle вычерчивает прямоугольник. Инструкция вызова метода
в общем виде выглядит так:
Объект->Canvas->Rectangle(x1,y1,x2,y2);
Параметры x1,y1 и x2,y2 задают координаты углов прямоугольника. Цвет,
ширину и стиль линии контура определяют, соответственно, значения
свойств Pen.Color, Pen.Width и Pen.Style, а цвет и стиль заливки внутренней
области — значения свойств Brush.Color и Brush.Style той поверхности, на
которой метод рисует.
В листинге 4.4 приведена функция обработки события Paint, которая на по-
верхности формы рисует итальянский флаг.
158 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Листинг 4.4. Метод Rectangle

void __fastcall TForm1::FormPaint(TObject *Sender)


{
int x;

// Итальянский флаг
Canvas->Brush->Color = clGreen;
Canvas->Rectangle(20,20,46,70);
Canvas->Brush->Color = clWhite;
Canvas->Rectangle(45,20,71,70);
Canvas->Brush->Color = clRed;
Canvas->Rectangle(70,20,96,70);

// контур
Canvas->Pen->Color = clBlack;
Canvas->Brush->Style = bsClear; // "прозрачная" кисть
Canvas->Rectangle(20,20,96,70);
}

В качестве параметра метода Rectangle можно указать структуру типа Trect,


поля left и top которой будут задавать координаты левого верхнего угла
прямоугольника, а поля right и bottom — правого нижнего. Задать положе-
ние и размер области (прямоугольника) можно обычным образом, присвоив
значения полям структуры TRect или вызвав функцию Rect.
Ниже приведен фрагмент кода, который демонстрирует различные способы
инициализации структуры TRect и ее использование в качестве параметра
метода Rectangle.
void __fastcall TForm1::FormPaint(TObject *Sender)
{
TRect aRect; // прямоугольная область
TPoint p1,p2; // точки, соответствующие углам области

aRect.Left = 10;
aRect.Top = 20;
aRect.Right = 30;
aRect.Bottom = 40;
Canvas->Rectangle(aRect);

aRect = Rect(50,20,70,40);
Canvas->Rectangle(aRect);
Ãëàâà 4. Ãðàôèêà 159

p1.x = 90;
p1.y = 20;
p2.x = 110;
p2.y = 40;
aRect = Rect(p1,p2);
Canvas->Rectangle(aRect);
}

Если нужно нарисовать только контур прямоугольника или закрашенный


прямоугольник, цвет границы которого совпадает с цветом закраски, то вме-
сто метода Rectangle удобно использовать, соответственно, метод FrameRect
или FillRect. Следует обратить внимание, что для рисования контура метод
FrameRect использует кисть, а не перо.
Следующая программа (ее текст приведен в листинге 4.5) демонстрирует ис-
пользование методов FillRect и FrameRect.

Листинг 4.5. Методы FillRect и FrameRect

void __fastcall TForm1::FormPaint(TObject *Sender)


{
TRect r;

// Французский флаг
// рисуем методами FillRect и FrameRect

// синяя полоса
Canvas->Brush->Color = clBlue;
r = Rect(140,20,165,70);
Canvas->FillRect(r);

// белая полоса
Canvas->Brush->Color = clWhite;
r.Left = 165;
r.Right = 190;
Canvas->FillRect(r);

// красная полоса
Canvas->Brush->Color = clRed;
r.Left = 190;
r.Right = 215;
Canvas->FillRect(r);
160 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

// контур
Canvas->Brush->Color = clBlack;
r.Left = 140;
r.Right = 215;
Canvas->FrameRect(r);
}

Метод RoundRect вычерчивает прямоугольник со скругленными углами. Ин-


струкция вызова метода RoundRect в общем виде выглядит так:
Объект->Canvas->RoundRect(x1,y1,x2,y2,x3,y3);

Параметры x1,y1 и x2,y2 определяют положение углов прямоугольника, а


параметры x3,y3 — размер эллипса, одна четверть которого используется для
вычерчивания скругленного угла (рис. 4.6).

Рис. 4.6. Метод RoundRect вычерчивает прямоугольник со скругленными углами

Ìíîãîóãîëüíèê
Метод Polygon вычерчивает многоугольник. Инструкция вызова метода в
общем виде выглядит так:
Объект->Canvas->Polygon(p,n)

где:
 p — массив типа TPoint, который содержит координаты вершин много-
угольника;
 n — количество вершин.
Метод Polygon чертит многоугольник, соединяя прямыми линиями точки,
координаты которых находятся в массиве: первую со второй, вторую с треть-
ей, третью с четвертой и т. д. Цвет, вид и ширину линии контура определяют,
соответственно, значения свойств Pen.Color, Pen.Width и Pen.Style, а цвет
и стиль заливки внутренней области — значения свойств Brush.Color и
Brush.Style той поверхности, на которой метод рисует.
Ãëàâà 4. Ãðàôèêà 161

В листинге 4.6 приведен фрагмент кода, который, используя метод Polygon,


рисует флажок.

Листинг 4.6. Метод Polygon

void __fastcall TForm1::PaintBox1Paint(TObject *Sender)


{
TPoint p[3]; // координаты точек перегиба
int dx,dy; // шаг сетки

// координаты будем отсчитывать от верхней точки древка


// флажок
p[0].x = 10;
p[0].y = 10;
p[1].x = p[0].x + 45;
p[1].y = p[0].y + 15;
p[2].x = p[0].x;
p[2].y = p[0].y + 30;

PaintBox1->Canvas->Brush->Color = clRed;

// чтобы не было контура у флажка, нужно указать,


// что линию рисовать не надо (Pen->Style = psClear)
// или задать цвет линии такой же, что и цвет закраски
PaintBox1->Canvas->Pen->Style = psClear; // контур не рисовать
//PaintBox1->Canvas->Pen->Color = clRed;

PaintBox1->Canvas->Polygon(p,2); // флажок

// древко
PaintBox1->Canvas->Pen->Style = psSolid;
PaintBox1->Canvas->MoveTo(p[0].x, p[0].y);
PaintBox1->Canvas->LineTo(p[0].x, p[2].y + 20);
}

Îêðóæíîñòü è ýëëèïñ
Нарисовать эллипс или окружность (частный случай эллипса) можно при по-
мощи метода Ellipse. Инструкция вызова метода в общем виде выглядит
следующим образом:
Объект->Canvas->Ellipse(x1,y1,x2,y2)
162 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Параметры x1,y1,x2,y2 определяют координаты прямоугольника, внутри


которого вычерчивается эллипс или (если прямоугольник является квадра-
том) окружность (рис. 4.7).

Рис. 4.7. Значения параметров метода Ellipse определяют вид


геометрической фигуры

Вместо четырех параметров (координат диагональных углов прямоугольни-


ка) методу Ellipse можно передать один — объект типа TRect.
Следующий фрагмент кода демонстрирует использование объекта TRect
в качестве параметра метода Ellipse.
Trect aRect;
aRect = Rect(10,10,60,60); // задать положение и размер области
PaintBox1->Canvas->Ellipse(aRect);

Как и в случае вычерчивания других примитивов, вид контура эллипса (цвет,


толщину и стиль линии) определяют значения свойства Pen, а цвет и стиль
заливки области внутри эллипса — значения свойства Brush той поверхности
(Canvas), на которой метод чертит.

Äóãà
Метод Arc рисует дугу — часть эллипса. Инструкция вызова метода в общем
виде выглядит так:
Объект->Canvas->Arc(x1,y1,x2,y2,x3,y3,x4,y4);

где:
 x1,y1,x2,y2 — параметры, определяющие эллипс, частью которого явля-
ется дуга;
 x3,y3 — параметры, определяющие начальную точку дуги;
 x4,y4 — параметры, определяющие конечную точку дуги.
Начальная и конечная точки дуги — это точки пересечения границы эллипса
и прямой, проведенной из центра эллипса в (x3,y3) и (x4,y4). Следует обра-
Ãëàâà 4. Ãðàôèêà 163

тить внимание: дуга чертится против часовой стрелки (рис. 4.8) — от точки
(x3,y3) к точке (x4,y4).
Цвет, толщина и стиль линии, вычерчивающей дугу, определяются значе-
ниями свойства Pen поверхности, на которой рисует метод.

Рис. 4.8. Метод Arc рисует дугу от точки (x3,y3) к точке (x4,y4)

Ñåêòîð
Метод Pie вычерчивает сектор эллипса или круга. Инструкция вызова метода
в общем виде выглядит следующим образом:
Объект->Canvas->Pie(x1,y1,x2,y2,x3,y3,x4,y4);

Параметры x1,y1,x2,y2 определяют эллипс, частью которого является сек-


тор; параметры x3,y3,x4,y4 определяют границы сектора (начальная точка
границ совпадает с центром эллипса). Сектор вырезается против часовой
стрелки от прямой, заданной точкой с координатами (x3,y3), к прямой, задан-
ной точкой с координатами (x4,y4) (рис. 4.9).

Рис. 4.9. Метод Pie рисует сектор от точки (x3,y3) к точке (x4,y4)

Òî÷êà
Свойство Pixels объекта Canvas представляет собой двумерный массив типа
TСolor, который содержит информацию о цвете точек графической поверх-
164 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

ности. Значением элемента массива Pixels является код цвета точки (значе-
ние типа TColor). Первый индекс элемента массива определяет горизонталь-
ную координату точки, второй — вертикальную. Размер массива Pixels со-
ответствует размеру графической поверхности.
Элемент Pixels[0][0] соответствует левой верхней точке графической по-
верхности, элемент Pixels[Canvas->Width-1][Canvas->Height-1] — правой
нижней.
Присвоив значение элементу массива Pixels, можно изменить цвет точки
графической поверхности, т. е. "нарисовать" или "стереть" точку. Например,
инструкция
PaintBox1->Canvas->Pixels[10][10] = clRed;

окрашивает точку поверхности в красный цвет.

Áèòîâûå îáðàçû
Для формирования сложных изображений используют битовые образы. Би-
товый образ — это картинка, которая находится в оперативной памяти.
Битовый образ можно загрузить из bmp-файла, из ресурса или же сформиро-
вать во время работы программы путем копирования из другого битового об-
раза или с графической поверхности.
Битовый образ — это объект типа TBitmap. Некоторые свойства класса
TBitmap приведены в табл. 4.7.

Òàáëèöà 4.7. Ñâîéñòâà êëàññà TBitmap

Свойство Описание

Height, Width Размер (ширина, высота) битового образа. Значения свойств


соответствуют размеру загруженной из файла (метод
LoadFromFile) или ресурса (метод LoadFromResourceID или
LoadFromResourceName) картинки
Empty Признак того, что картинка в битовый образ не загружена
(true)
Transparent Устанавливает (true) режим использования "прозрачного"
цвета. При выводе битового образа методом Draw элемен-
ты картинки, цвет которых совпадает с цветом
TransparentColor, не выводятся. По умолчанию значение
TransparentColor определяет цвет левого нижнего пиксела
TransparentColor Задает прозрачный цвет. Элементы картинки, окрашенные
этим цветом, методом Draw не выводятся
Ãëàâà 4. Ãðàôèêà 165

Òàáëèöà 4.7 (îêîí÷àíèå)

Свойство Описание
Canvas Поверхность битового образа, на которой можно рисовать
точно так же, как на поверхности формы или компонента
PaintBox

Çàãðóçêà áèòîâîãî îáðàçà èç ôàéëà


Загрузку битового образа из файла обеспечивает метод LoadFromFile, которо-
му в качестве параметра передается имя bmp-файла. Например, следующий
фрагмент кода обеспечивает создание и загрузку битового образа из файла
plane.bmp.
Graphics::TBitmap *bm; // битовый образ
bm = new Graphics::Tbitmap; // создать объект TBitmap
bm->LoadFromFile("plane.bmp"); // загрузить картинку

В результате выполнения приведенного фрагмента битовый образ bm пред-


ставляет собой изображение самолета (предполагается, что в файле plane.bmp
находится изображение самолета, а не что-либо другое).

Îòîáðàæåíèå áèòîâîãî îáðàçà


Загруженный из файла или из ресурса (см. далее) битовый образ можно вы-
вести на графическую поверхность формы или компонента PaintBox. Ото-
бражение битовых образов обеспечивают методы Draw и CopyRect.

Ìåòîä Draw
В качестве параметров этого метода указывают координаты точки графиче-
ской поверхности, от которой надо вывести битовый образ, и сам битовый
образ. Метод выводит картинку целиком. Например, инструкция
Form1->Canvas->Draw(10,20,bm);

выводит на поверхность формы битовый образ bm.


Если свойству Transparent битового образа присвоить значение true, то
фрагменты битового образа, цвет которых совпадает с цветом его левой ниж-
ней точки, не будут выведены. Такой прием используется для создания эф-
фекта прозрачного фона. Прозрачный цвет можно задать напрямую, присвоив
значение свойству Transparent.Color.
Следующая программа (ее окно приведено на рис. 4.10) демонстрирует за-
грузку и отображение битовых образов.
166 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 4.10. Изображение неба и самолета — битовые образы, загруженные из файлов

После запуска программы в ее окне появляется изображение самолета, летя-


щего в облаках. И небо, и самолет — это битовые образы, загруженные из
файлов. Загрузку битовых образов выполняет функция обработки события
Creatе формы, отображение — функция обработки события Paint. Объявле-
ние битовых образов следует поместить в h-файл, в секцию private объявле-
ния формы (листинг 4.7). Конструктор формы и функция обработки события
Paint приведены в листинге 4.8.

Листинг 4.7. Объявление битовых образов (h-файл)

class TForm1 : public TForm


{
__published: // IDE-managed Components
void __fastcall FormPaint(TObject *Sender);

private: // User declarations


// Битовые образы
Graphics::TBitmap *sky; // небо
Graphics::TBitmap *plane; // самолет
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};

Листинг 4.8. Загрузка и отображение битовых образов

// конструктор формы
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
Ãëàâà 4. Ãðàôèêà 167

{
// создать два битовых образа
// и загрузить картинки
sky = new Graphics::TBitmap;
sky->LoadFromFile("sky.bmp");
plane = new Graphics::TBitmap;
plane->LoadFromFile("plane.bmp");
plane->Transparent = true;

// установить размер клиентской области окна


// равным размеру фонового рисунка
Form1->ClientWidth = sky->Width;
Form1->ClientHeight = sky->Height;
}

// обработка события Paint


void __fastcall TForm1::FormPaint(TObject *Sender)
{
// сформировать картинку
Form1->Canvas->Draw(0,0,sky); // фон — небо
Form1->Canvas->Draw(150,50,plane); // объект — самолет
}

Битовый образ можно использовать для формирования фонового рисунка по


принципу кафельной плитки (рис. 4.11, а и б).

а б
Рис. 4.11. Фоновый рисунок (а) и битовый образ (б), из которого он составлен

Следующая программа (листинг 4.9) показывает, как сформировать фоновый


рисунок путем многократного вывода битового образа на поверхность фор-
мы. Формирование рисунка (многократный вывод образа на поверхность
168 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

формы) выполняет функция обработки события Paint формы, загрузку бито-


вых образов — конструктор формы. Объявление битового образа (Graphics::
TBitmap *bm) надо поместить в h-файл, в секцию private.

Листинг. 4.9. Фоновый рисунок

// конструктор
__fastcall TForm1::TForm1(TComponent* Owner): TForm(Owner)
{
// объявление bm надо поместить в секцию
// private h-файла
bm = new Graphics::TBitmap(); // создать объект — битовый образ

// загрузить картинку
try // в процессе загрузки картинки возможны ошибки
{
Form1->bm->LoadFromFile("puzzle.bmp");
}
catch (EFOpenError &e)
{
return;
}
}

// Формирует фоновый рисунок.


// Объявление этой функции надо поместить в секцию
// private h-файла
void __fastcall TForm1::Background()
{
int x=0,y=0; // координаты левого верхнего угла битового образа

if ( bm->Empty ) // битовый образ не был загружен


{
Form1->Canvas->TextOut(10,10,"Битовый образ не загружен");
return;
}

do {
do {
Canvas->Draw(x,y,bm);
x += bm->Width;
}
Ãëàâà 4. Ãðàôèêà 169

while (x < ClientWidth);


x = 0;
y += bm->Height;
}
while (y < ClientHeight);
}

// обработка события Paint


void __fastcall TForm1::FormPaint(TObject *Sender)
{
Background(); // отобразить фоновый рисунок
}

// обработка события Resize


void __fastcall TForm1::FormResize(TObject *Sender)
{
Background(); // отобразить фоновый рисунок
}

Ìåòîä CopyRect
Этот метод позволяет вывести на графическую поверхность фрагмент бито-
вого образа, он копирует картинку с одной графической поверхности на
другую.
Инструкция вызова метода CopyRect в общем виде выглядит так:
Объект->Canvas1->CopyRect(Область1, Canvas2, Область2)

где:
 Canvas1 — поверхность, на которую копируется картинка;
 Canvas2 — поверхность, с которой копируется картинка;
 Область1 — структура типа TRect, которая задает положение и размер об-
ласти, куда выполняется копирование;
 Область2 — структура типа TRect, которая задает положение и размер об-
ласти, откуда выполняется копирование.
Следующая программа (ее текст приведен в листингах 4.10 и 4.11) выводит
на поверхность окна баннер — рекламное сообщение, представляющее собой
последовательность сменяющих друг друга картинок (кадров). На рис. 4.12
приведены кадры баннера (содержимое файла baner.bmp), а на рис. 4.13 —
окно программы.
170 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 4.12. Кадры баннера

Рис. 4.13. Воспроизведение баннера обеспечивает метод CopyRect

Листинг 4.10. Баннер (h-файл)

class TForm1 : public TForm


{
__published:
TTimer *Timer1;
void __fastcall FormCreate(TObject *Sender);
void __fastcall Timer1Timer(TObject *Sender);
private:
Graphics::TBitmap *baner; // баннер
TRect kadr; // кадр баннера
TRect scr; // область воспроизведения баннера
int w, h; // размер кадра
int c; // номер воспроизводимого кадра

public:
__fastcall TForm1(TComponent* Owner);
};

Листинг 4.11. Баннер (cpp-файл)

#define FBANER "borland.bmp" // баннер


#define NKADR 4 // количество кадров в баннере

void __fastcall TForm1::FormCreate(TObject *Sender)


Ãëàâà 4. Ãðàôèêà 171

{
baner = new Graphics::TBitmap();
baner->LoadFromFile(FBANER); // загрузить баннер

h = baner->Height;
w = baner->Width / NKADR;

scr = Rect(10,10,10+w,10+h); // положение и размер области


// воспроизведения баннера
kadr = Rect(0,0,w,h); // положение и размер первого кадра в баннере
}

// обработка события OnTimer


void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// вывести кадр баннера
Canvas->CopyRect(scr,baner->Canvas,kadr);

// подготовиться к воспроизведению следующего кадра


if (c < NKADR)
{
// воспроизводимый в данный момент
// кадр не последний
c++;
kadr.Left += w;
kadr.Right += w;
}
else
{
c = 0;
kadr.Left = 0;
kadr.Right = w;
}
}

Программа состоит из двух функций. Функция TForm1::FormCreate создает


объект TBitmap и загружает в него баннер — bmp-файл, в котором находятся
кадры баннера. Затем, используя информацию о размере загруженного бито-
вого образа, функция устанавливает значения характеристик кадра — высоту
и ширину.
Основную работу в программе выполняет функция обработки события Timer,
которая выделяет из битового образа baner очередной кадр и выводит его на
172 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

поверхность формы. Выделение кадра и его вывод на поверхность формы


путем копирования фрагмента картинки с одной поверхности на другую вы-
полняет метод CopyRect (рис. 4.14), которому в качестве параметров переда-
ются координаты области, куда нужно копировать (Rect1), поверхность
(Canvas2) и положение области, откуда нужно копировать (Rect2). Положение
кадра в "фильме" (координата х левого верхнего угла) вычисляется умноже-
нием ширины кадра на его номер.

Rect1 – область, в которой Canvas2 – невидимый объект TBitmap.


отображаются кадры баннера Содержит все кадры баннера

Rect2 – текущий кадр

Canvas1 – поверхность, на которой


отображаются кадры баннера

Рис. 4.14. Метод CopyRect копирует в область Rect1 поверхности Canvas1 область Rect2
с поверхности Canvas2

Çàãðóçêà áèòîâîãî îáðàçà èç ðåñóðñà


В предыдущих примерах битовые образы загружались из файлов. У этого
решения есть недостаток: если по какой-либо причине файлы, в которых на-
ходятся битовые образы, станут недоступными, программа не будет работать.
Существует возможность поместить битовые образы в exe-файл и по мере
необходимости загружать их оттуда (именно таким образом загружаются
картинки для командных кнопок в большинстве программ).
Битовый образ, находящийся в exe-файле, называется ресурсом, а операция
загрузки такого битового образа — загрузкой из ресурса.
Для того чтобы воспользоваться возможностью загрузки битового образа из
ресурса, надо создать файл ресурсов, поместить в него необходимые картин-
ки и сообщить компилятору о необходимости включить содержимое файла
ресурсов в exe-файл.
Ãëàâà 4. Ãðàôèêà 173

Ôàéë ðåñóðñîâ
Создать файл ресурсов и поместить в него необходимые картинки можно
с помощью утилит Borland Image Editor, Borland Resource Workshop или дру-
гой программы, которая позволяет создать res-файл.
Процесс создания файла ресурсов рассмотрим на примере Borland Image
Editor. Чтобы создать файл ресурсов, надо запустить Borland Image Editor и в
меню File выбрать команду NewResource File (рис. 4.15). В результате бу-
дет создан новый, пока еще не содержащий ни одного ресурса, файл ресурсов
Untitled.res. Чтобы в созданный файл ресурсов добавить битовый образ, надо
в меню Resource выбрать команду NewBitmap и в появившемся окне
Bitmap Properties задать характеристики битового образа (рис. 4.16). В ре-
зультате описанных действий в файл ресурсов будет добавлен битовый образ
и его имя (Bitmap1) появится в списке ресурсов. Имя созданного ресурса
можно изменить (для этого в меню Resource надо выбрать команду Rename).

Рис. 4.15. Чтобы создать файл ресурсов, надо в меню File выбрать команду
NewResource File

Рис. 4.16. В окне Bitmap Properties надо задать характеристики битового образа
174 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

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


совать нужную картинку. Чтобы начать рисовать, надо активизировать режим
редактирования битового образа — в меню Resource выбрать команду Edit.
Графический редактор Image Editor предоставляет разработчику стандартный
для подобных редакторов набор инструментов, используя которые можно
нарисовать нужную картинку.
Если нужная картинка уже существует в виде отдельного файла, то ее можно
через буфер обмена поместить в битовый образ. Перед тем как выполнить
команду Вставить (EditPast), необходимо установить значения характери-
стик битового образа в соответствии с характеристиками картинки, находя-
щейся в буфере обмена. Чтобы это сделать, надо в меню Bitmap выбрать
команду Image Properties и в появившемся окне задать характеристики би-
тового образа.
После того как все битовые образы будут помещены в файл ресурсов, его
следует сохранить в том каталоге, где находится программа, для которой этот
файл создается. Делается это обычным образом — выбором в меню File
команды Save.
В качестве примера на рис. 4.17 приведено окно Image Editor во время работы
с файлом images.res. Файл содержит два битовых образа: PLANE и SKY.

Рис. 4.17. Файл ресурсов images.res содержит два битовых образа

Ïîäêëþ÷åíèå ôàéëà ðåñóðñîâ


Для того чтобы компилятор поместил в exe-файл битовые образы, находя-
щиеся в файле ресурсов, в проект надо добавить файл ресурсов — выбрать в
Ãëàâà 4. Ãðàôèêà 175

меню Project команду Add to Project и в открывшемся окне указать имя


res-файла. В результате этих действий выбранный res-файл появится в списке
файлов проекта, который отображается в окне Project Manager. Следует об-
ратить внимание, что в списке файлов проекта есть res-файл, имя которого
совпадает с именем проекта. В этом файле находится значок приложения,
представляющий собой ico-ресурс или, как принято говорить в среде про-
граммистов, иконку.

Çàãðóçêà áèòîâîãî îáðàçà


Загрузку битового образа из ресурса обеспечивает метод LoadFromResourceName.
В качестве параметров метода надо указать идентификатор программы и имя
ресурса.
В качестве примера в листинге 4.12 приведен конструктор формы, который
загружает битовые образы из ресурса.

Листинг 4.12. Загрузка битовых образов из ресурса

// конструктор
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
sky = new Graphics::TBitmap;
sky->LoadFromResourceName((int)HInstance,"SKY");
ClientWidth = sky->Width;
ClientHeight = sky->Height;

plane = new Graphics::TBitmap;


plane->LoadFromResourceName((int)HInstance,"PLANE");
plane->Transparent = true;
}

Преимущества загрузки битовых образов из ресурса очевидны, все необхо-


димые программе картинки находятся в исполняемом файле.

Àíèìàöèÿ
Анимация (дословно — оживление) — это технология создания "живых"
изображений. Анимированное изображение или анимация, в отличие от
обычной картинки, представляет собой изображение, элементы которого ве-
дут себя подобно объектам реального мира.
Существуют два подхода к реализации анимации. Первый предполагает на-
личие заранее подготовленной серии картинок (кадров), где изображены
фазы движения объекта, последовательное отображение которых и создает
эффект анимации. Этот подход используют создатели мультфильмов. Второй
176 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

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


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

Äâèæåíèå
Движение — один из основных анимационных эффектов. Реализовать его не
сложно: сначала нужно вывести изображение объекта, затем через некоторое
время стереть и снова вывести, но уже на небольшом расстоянии от его пер-
воначального положения. Подбором времени между выводом и удалением
изображения, а также расстоянием между новым и предыдущим положением
объекта можно добиться того, что у наблюдателя будет складываться впечат-
ление равномерного движения объекта.
Следующая программа (в ее окне "плывет" корабль (рис. 4.18)) показывает,
как можно реализовать эффект перемещения (движения) объекта.

Рис. 4.18. Окно программы

Корабль на поверхности формы рисует функция Ship. В качестве параметров


она получает координаты базовой точки. Базовая точка (x0,y0) определяет
положение объекта в целом, от нее отсчитываются координаты графических
примитивов, образующих объект (рис. 4.19). Координаты графических при-
митивов лучше отсчитывать не в пикселах, а в приращениях. Такой подход
позволяет легко масштабировать изображение.
Форма программы приведена на рис. 4.20. Компонент Timer обеспечивает
генерацию события Timer, функция обработки которого выполняет основную
работу — стирает изображение объекта и рисует его на новом месте.
Настройку компонента Timer выполняет конструктор формы. Он же задает
размер и исходное положение корабля, а также скорость его движения (ско-
Ãëàâà 4. Ãðàôèêà 177

рость определяется периодом возникновения события Timer и величиной


приращения координаты х).

Рис. 4.19. Базовая точка (x0,y0) определяет положение объекта

Рис. 4.20. Форма программы

В листинге 4.13 приведен h-файл программы. Конструктор, функция Ship и


функция обработки события Timer приведены в листинге 4.14.

Листинг 4.13. Движение (h-файл)

class TForm1 : public TForm


{
__published: // IDE-managed Components
TTimer *Timer1;
void __fastcall Timer1Timer(TObject *Sender);
private:
int x,y; // текущее положение объекта (базовой точки)
void __fastcall Ship(int x, int y); // рисует объект (корабль)
178 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

public: // User declarations


__fastcall TForm1(TComponent* Owner);
};

Листинг 4.14. Движение (cpp-файл)

// конструктор
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// начальное положение корабля (базовой точки)
x = -70;
y = 100;

/* Таймер можно настроить во время разработки программы


(в процессе создания формы) или во время работы программы. */

// настройка и запуск таймера


Timer1->Interval = 50; // период события Timer — 0.1 сек.
Timer1->Enabled = true; // пуск таймера
}

// рисует на поверхности формы кораблик


void __fastcall TForm1::Ship(int x, int y)
{
int dx=3,dy=3; // шаг сетки
// корпус и надстройку будем рисовать
// при помощи метода Polygon
TPoint p1[7]; // координаты точек корпуса
TPoint p2[8]; // координаты точек надстройки

TColor pc,bc; // текущий цвет карандаша и кисти

// сохраним текущий цвет карандаша и кисти


pc = Canvas->Pen->Color;
bc = Canvas->Brush->Color;

// установим нужный цвет карандаша и кисти


Canvas->Pen->Color = clBlack;
Canvas->Brush->Color = clWhite;

// рисуем ..
// корпус
p1[0].x = x; p1[0].y = y;
p1[1].x = x; p1[1].y = y-2*dy;
Ãëàâà 4. Ãðàôèêà 179

p1[2].x = x+10*dx; p1[2].y = y-2*dy;


p1[3].x = x+11*dx; p1[3].y = y-3*dy;
p1[4].x = x+17*dx; p1[4].y =y-3*dy;
p1[5].x = x+14*dx; p1[5].y =y;
p1[6].x = x; p1[6].y =y;
Canvas->Polygon(p1,6);

// надстройка
p2[0].x = x+3*dx; p2[0].y = y-2*dy;
p2[1].x = x+4*dx; p2[1].y = y-3*dy;
p2[2].x = x+4*dx; p2[2].y = y-4*dy;
p2[3].x = x+13*dx; p2[3].y = y-4*dy;
p2[4].x = x+13*dx; p2[4].y = y-3*dy;
p2[5].x = x+11*dx; p2[5].y = y-3*dy;
p2[6].x = x+10*dx; p2[6].y = y-2*dy;
p2[7].x = x+3*dx; p2[7].y = y-2*dy;
Canvas->Polygon(p2,7);

Canvas->MoveTo(x+5*dx,y-3*dy);
Canvas->LineTo(x+9*dx,y-3*dy);

// капитанский мостик
Canvas->Rectangle(x+8*dx,y-4*dy,x+11*dx,y-5*dy);

// труба
Canvas->Rectangle(x+7*dx,y-4*dy,x+8*dx,y-7*dy);

// иллюминаторы
Canvas->Ellipse(x+11*dx,y-2*dy,x+12*dx,y-1*dy);
Canvas->Ellipse(x+13*dx,y-2*dy,x+14*dx,y-1*dy);

// мачта
Canvas->MoveTo(x+10*dx,y-5*dy);
Canvas->LineTo(x+10*dx,y-10*dy);

// оснастка
Canvas->Pen->Color = clWhite;
Canvas->MoveTo(x+17*dx,y-3*dy);
Canvas->LineTo(x+10*dx,y-10*dy);
Canvas->LineTo(x,y-2*dy);

// восстановим цвет карандаша и кисти


Canvas->Pen->Color = pc;
180 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Canvas->Brush->Color = bc;
}

// обработка события Timer


void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// стереть кораблик — закрасить цветом, совпадающим
// с цветом фона (формы)
Canvas->Brush->Color = Form1->Color;
Canvas->FillRect(Rect(x-1,y+1,x+68,y-40));

// вычислить координаты базовой точки


x+=3;
if (x > ClientWidth) {
// кораблик уплыл за правую границу формы
x= -70; // чтобы кораблик "выплывал" из-за левой границы формы
y=random(Form1->ClientHeight);
}
// нарисовать кораблик на новом месте
Ship(x,y);
}

Èñïîëüçîâàíèå áèòîâûõ îáðàçîâ


В предыдущем примере изображение объекта формировалось из графических
примитивов. Недостаток такого способа очевиден: чтобы сформировать бо-
лее-менее реалистичную картинку, необходимо обеспечить отображение
большого количества графических примитивов, что существенно увеличива-
ет размер кода, снижает скорость работы программы (именно поэтому разра-
ботчики компьютерных игр используют специальные библиотеки). Теперь на
примере программы Полет в облаках рассмотрим, как можно существенно
улучшить графику за счет использования битовых образов.
Как и в предыдущих программах, эффект движения (полет самолета) дости-
гается за счет периодической перерисовки объекта с некоторым смещением
относительно его прежнего положения.
Перед выводом картинки в новой точке предыдущее изображение должно
быть удалено. Удалить изображение объекта можно путем перерисовки всей
фоновой картинки или только той ее части, которая перекрыта объектом.
В рассматриваемой программе реализован второй способ.
Форма программы Полет в облаках приведена на рис. 4.21, текст — в лис-
тингах 4.15 и 4.16.
Ãëàâà 4. Ãðàôèêà 181

Рис. 4.21. Форма программы Полет в облаках

Листинг 4.15. Полет в облаках (h-файл)

class TForm1 : public TForm


{
__published: // IDE-managed Components
TTimer *Timer1;
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall FormPaint(TObject *Sender);
private: // User declarations
Graphics::TBitmap *back; // фон
Graphics::TBitmap *plane; // объект
int x,y; // координаты объекта

public: // User declarations


__fastcall TForm1(TComponent* Owner);
};

Листинг 4.16. Полет в облаках (cpp-файл)

// Конструктор
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
back = new Graphics::TBitmap;
back->LoadFromFile("sky.bmp");
// установить размер формы в соответствии
// с размером фонового рисунка
Form1->ClientWidth = back->Width;
Form1->ClientHeight = back->Height;
182 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

plane = new Graphics::TBitmap;


plane->LoadFromFile("plane.bmp");
plane->Transparent = true;

// исходное положение объекта


x = -30;
y = 70;

Timer1->Interval = 25;
Timer1->Enabled = true;
}

// Сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
TRect r; // область, в которой находится объект

// стереть объект (восстановить фон)


r = Rect(x,y,x+plane->Width,y+plane->Height);
Canvas->CopyRect(r,back->Canvas,r);

x = x + 2;

// нарисовать объект на новом месте


Canvas->Draw(x,y,plane);

if ( x > Form1->Width + plane->Width + 10) {


x = -20;
}
}

// обработка события Paint


void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Draw(0,0,back);
Canvas->Draw(x,y,plane);
}

Конструктор (функция обработки события Create) загружает битовые образы


(фон и изображение объекта), устанавливает размер формы в соответствии с
размером фонового рисунка и задает начальное положение объекта. Следует
обратить внимание на то, что начальное значение переменной x, которая оп-
ределяет положение левой верхней точки области вывода изображения объ-
Ãëàâà 4. Ãðàôèêà 183

екта — отрицательное число, по модулю больше ширины битового образа


объекта. Поэтому в начале работы программы самолет не виден.
С каждым сигналом от таймера значение координаты x увеличивается, и на
экране появляется та часть битового образа, координаты которой больше ну-
ля. Таким образом, у наблюдателя создается впечатление, что самолет выле-
тает из-за левой границы окна.
Основную работу (перерисовку объекта) выполняет функция обработки сиг-
нала от таймера (события Timer). Сначала она стирает изображение объекта
(восстанавливает ''испорченную" часть фона), затем выводит изображение
объекта на новом месте.
Восстановление фона выполняет метод CopyRect путем копирования фраг-
мента фона (битового образа back) в ту область графической поверхности, в
которой в данный момент находится объект (рис. 4.22).
Функция обработки события Paint обеспечивает отображение фона в начале
работы программы, а также всякий раз после того, как окно программы появ-
ляется на экране.

Рис. 4.22. Восстановление фона перед отрисовкой объекта на новом месте


обеспечивает метод CopyRect

Запустив программу Полет в облаках, можно заметить, что изображение са-


молета мерцает. Это объясняется тем, что глаз успевает заметить, как самолет
исчез и появился снова. Чтобы устранить мерцание, надо чтобы самолет не
исчезал, а смещался. Добиться этого можно, если формировать изображение
не на поверхности формы, а на невидимой для пользователя графической по-
верхности, а затем выводить готовое изображение на поверхность формы.
Рис. 4.23 поясняет процесс формирования изображения. Сначала фрагмент
фона копируется на невидимую пользователю поверхность frame (шаг 1), за-
тем на эту поверхность выводится изображение объекта (шаг 2), после чего
сформированное на поверхности frame изображение выводится в нужную
184 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

точку видимой графической поверхности (шаг 3). Приведенная в листин-


ге 4.17 программа демонстрирует реализацию описанного метода формиро-
вания изображения.

Рис. 4.23. Формирование и отображение кадра

Листинг 4.17. Формирование изображения на невидимой поверхности

// конструктор
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
try {
// загрузить фоновый рисунок из bmp-файла
back = new Graphics::TBitmap();
back->LoadFromFile("sky.bmp");

// загрузить картинку
plane = new Graphics::TBitmap();
plane->LoadFromFile("plane.bmp");
plane->Transparent = true;
}
catch (EFOpenError &e) {
Timer1->Enabled = false;
return;
}

// установить размер клиентской (рабочей) области формы


// в соответствии с размером фонового рисунка
ClientWidth = back->Width;
ClientHeight = back->Height;
Ãëàâà 4. Ãðàôèêà 185

// сформировать кадр
frame = new Graphics::TBitmap();
frame->LoadFromFile("plane.bmp");

// исходное положение самолета


x=-40; // чтобы самолет "вылетал" из-за левой границы окна
y=60;

Timer1->Interval = 10;
Timer1->Enabled = true;
}

// сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
TRect badRect; // положение и размер области фона,
// которую надо восстановить
TRect frameRect;

badRect = Rect(x,y,x+plane->Width,y+plane->Height);
frameRect = Rect(0,0, frame->Width, frame->Height);

// изображение формируем на рабочей поверхности,


// затем выводим на поверхность формы

// сформировать очередной кадр

// скопировать фрагмент фона


frame->Canvas->CopyRect(frameRect, back->Canvas,badRect);
// нарисовать объект
frame->Canvas->Draw(0,0,plane);

// вывести кадр
Form1->Canvas->Draw(x,y,frame);

// вычислим новые координаты спрайта


x += 1;
if (x > ClientWidth)
{
// самолет улетел за правую границу формы
// изменим высоту и скорость полета
x = -20;
y = random(ClientHeight — 30); // высота полета
186 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

// скорость полета определяется периодом возникновения


// события OnTimer, который, в свою очередь, зависит
// от значения свойства Interval
Timer1->Interval =random(20)+10; // скорость "полета" от 10 до 29
}
}

void __fastcall TForm1::FormPaint(TObject *Sender)


{
if ((!back->Empty) && (!plane->Empty))
{
Canvas->Draw(0,0,back);
}
else
Canvas->TextOut(10,10,"Ошибка загрузки битовых образов");
}

Âçàèìîäåéñòâèå ñ ïîëüçîâàòåëåì
Программист может позволить пользователю управлять движением объектов
в окне программы. Следующая программа показывает, как это сделать.
Программа ПВО представляет собой игру, цель которой — уничтожить са-
молеты противника. В окне программы отображаются два объекта: летящий
самолет и пусковая установка (рис. 4.24). Игрок, нажав пробел, может запус-
тить ракету, которая (если момент запуска выбран правильно) собьет само-
лет. Также с помощью клавиш перемещения курсора игрок может сместить
пусковую установку влево или вправо. Текст программы приведен в листин-
гах 4.18 и 4.19.

Рис. 4.24. Игра ПВО


Ãëàâà 4. Ãðàôèêà 187

Основную работу выполняет функция обработки события Timer, которая ри-


сует самолет, пусковую установку и снаряд. Сначала она сравнивает коорди-
наты самолета и снаряда. Если координаты самолета совпадают с координа-
тами снаряда, то функция стирает самолет, увеличивает счетчик попаданий и
завершает работу. Если снаряд не долетел до самолета, то функция перерисо-
вывает самолет на новом месте. Если ракета запущена (в этом случае значе-
ние переменной dy равно минус единице), то функция рисует ее.
Далее функция проверяет, надо ли перерисовать на новом месте пусковую
установку. Если игрок удерживает клавишу перемещения курсора, то функ-
ция перерисовывает пусковую установку со смещением относительно ее те-
кущего положения. Запуск ракеты обеспечивает функция обработки события
KeyPress. Она (если нажат пробел и если ракета не запущена) записывает в
переменную dy минус единицу (в результате функция обработки события
Timer рисует ракету).
Нажатие клавиш перемещения курсора обрабатывает функция обработки со-
бытия KeyDown формы (это событие генерируется до тех пор, пока пользова-
тель удерживает клавишу). Она, в зависимости от того, какую клавишу удер-
живает игрок, задает направление перемещения установки (записывает в пе-
ременную dx единицу или минус единицу).
Если значение dx не равно нулю (т. е. игрок удерживает клавишу перемеще-
ния курсора), то функция обработки события Timer рисует установку на но-
вом месте.
Если игрок отпустит клавишу, то возникает событие KeyUp, функция обработ-
ки которого записывает в переменную dx ноль, в результате чего установка
перестает двигаться.

Листинг 4.18. Игра ПВО (h-файл)

class TForm1 : public TForm


{
__published:
TTimer *Timer1;
void __fastcall Timer1Timer(TObject *Sender);
void __fastcall FormKeyPress(TObject *Sender, char &Key);
void __fastcall FormKeyDown(TObject *Sender, WORD &Key,
TShiftState Shift);
void __fastcall FormKeyUp(TObject *Sender, WORD &Key,
TShiftState Shift);
void __fastcall FormPaint(TObject *Sender);
188 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

private: // User declarations


TPoint us; // координаты установки (пушка)
TPoint sn; // координаты снаряда
TPoint pl; // координаты самолета
int dy; // приращение координаты Y снаряда
int dx; // приращение координаты X установки
int n; // количество выстрелов
int m; // количество попаданий

bool GameOver; // признак того, что игра закончена

void __fastcall info(); // выводит информацию


void __fastcall itog(); // выводит результат

public: // User declarations


__fastcall TForm1(TComponent* Owner);
};

Листинг 4.19. Игра ПВО (cpp-файл)

#define NG 10 // количество снарядов

// конструктор
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// исходное положение установки
us.x = ClientWidth / 2 — 25;
us.y = ClientHeight — 20;

// исходное положение самолета


pl.x = 0;
pl.y = 60;

Canvas->Pen->Color = Form1->Color;
Randomize;

Timer1->Interval = 10;
Timer1->Enabled = true;

GameOver = false;
}
Ãëàâà 4. Ãðàôèêà 189

// Сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// сравнить координаты самолета и снаряда
if ((pl.x > sn.x -5 ) && (pl.x < sn.x + 5) &&
(pl.y < sn.y +5) && (pl.y > sn.y -5))
{
// попадание, стереть самолет
Canvas->Brush->Color = Color;
Canvas->Rectangle(pl.x-5, pl.y-5, pl.x +10, pl.y+10);
pl.x = -20;
sn.x = us.x + 25;
dy =0;
m = m + 1; // количество попаданий

info(); // отобразить информацию


if (n == NG) {
// снаряды кончились
Timer1->Enabled = false;
GameOver = true;
itog();
}
else
pl.y = 60 + Random(50); // высота полета цели
return;
}

// стереть самолет
Canvas->Brush->Color = Form1->Color;
Canvas->Rectangle(pl.x, pl.y, pl.x +7, pl.y+5);

if (pl.x < ClientWidth)


pl.x = pl.x + 1;
else {
pl.x = — 20;
pl.y = 50 + random(50);
};

// нарисовать самолет на новом месте


Canvas->Brush->Color = clNavy;
Canvas->Rectangle(pl.x, pl.y, pl.x +7, pl.y+5);

// снаряд
if ( dy < 0 ) // снаряд летит?
190 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

{
// стереть снаряд
Canvas->Brush->Color = Form1->Color;
Canvas->Rectangle(sn.x, sn.y, sn.x +4, sn.y+7);
if ( sn.y > 0 )
{
sn.y = sn.y — 1;
// нарисовать снаряд на новом месте
Canvas->Brush->Color = clBlack;
Canvas->Rectangle(sn.x, sn.y, sn.x +4, sn.y+7);
}
else {

// снаряд долетел до верхней границы окна


dy = 0;
if (n == NG) {
// снаряды кончились
Timer1->Enabled = false;
GameOver = true;
itog();
return;
}
}
}

// установка
if (((dx < 0) && (us.x > 0)) ||
((dx > 0) && (us.x < ClientWidth — 50)))
{
// dx <> 0 — игрок удерживает клавишу
// "курсор вправо" или "курсор влево"
Canvas->Brush->Color = Form1->Color;
Canvas->Rectangle(us.x, us.y, us.x +50, us.y+7);
us.x = us.x + dx;
// нарисовать установку на новом месте
Canvas->Brush->Color = clBlack;
Canvas->Rectangle(us.x, us.y, us.x +50, us.y+7);
}
}

// нажатие клавиши
void __fastcall TForm1::FormKeyPress(TObject *Sender, char &Key)
{
Ãëàâà 4. Ãðàôèêà 191

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


if ((Key == ' ') && (dy == 0) && ( n< NG )) {
// пуск ракеты
sn.x = us.x + 25;
sn.y = ClientHeight — 30;
dy = -1;
n = n+1;
info();
return;
}

if (GameOver) {
Form1->Close();
}
}

// клавиша удерживается
void __fastcall TForm1::FormKeyDown(TObject *Sender,
WORD &Key, TShiftState Shift)
{
switch (Key) {
case VK_LEFT : dx = -1;
break;
case VK_RIGHT: dx = 1;
break;
}
}

// клавиша отпущена
void __fastcall TForm1::FormKeyUp(TObject *Sender,
WORD &Key, TShiftState Shift)
{
if ((Key == VK_LEFT) || (Key == VK_RIGHT))
dx = 0;
}

void __fastcall TForm1::FormPaint(TObject *Sender)


{
info();
Canvas->Brush->Color = clBlack;
Canvas->Rectangle(us.x, us.y, us.x +50, us.y+7);
info();
192 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

if ( GameOver) {
itog();
}
}

// информация
void __fastcall TForm1::info()
{
AnsiString st;

Canvas->Brush->Color = Form1->Color;
st = "Выстрелов: " + IntToStr(n);
Canvas->TextOut(10,10,st);
st = "Попаданий: " + IntToStr(m);
Canvas->TextOut(10,25,st);
}

void __fastcall TForm1::itog()


{
int x1,y1,x2,y2;
int x,y;
AnsiString st;
float r;

Canvas->Brush->Color = clRed;
Canvas->Pen->Color = clBlack;
x1 = (ClientWidth-200) / 2;
y1 = 50;
x2 = x1 + 200;
y2 = y1 + 70;
Canvas->FillRect( Rect(x1,y1,x2,y2) );

st = "Игра закончена";
Canvas->Font->Size = 14;
x= x1 + (x2 — x1 — Canvas->TextWidth(st))/2;
y = y1 +15;
Canvas->TextOut(x, y,st);

r = float(m) / float(n) * 100;


st = "Результативность: " + FloatToStr(r)+"%";
Canvas->Font->Size = 9;
x= x1 + (x2 — x1 — Canvas->TextWidth(st))/2;
y = y +20;
Canvas->TextOut(x, y,st);
}
ÃËÀÂÀ 5

Ìóëüòèìåäèà

Большинство современных программ являются мультимедийными, что под-


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

Ôóíêöèÿ PlaySound
Для реализации звуковых эффектов (например, в играх) весьма удобна функ-
ция PlaySound. Она позволяет проиграть звуковой фрагмент, находящийся в
wav-файле.
Инструкция вызова функции PlaySound в общем виде выглядит так:
PlaySound(wav-файл, 0, Режим);

Параметр wav-файл задает звуковой файл, параметр Режим — режим воспроиз-


ведения (синхронный или асинхронный). Если задан синхронный режим вос-
произведения, то функция PlaySound возвращает управление программе сра-
зу после того, как будет инициирован процесс воспроизведения звука. Если
задан асинхронный режим, то программа, вызвавшая функцию PlaySound,
продолжит работу только после того, как завершится воспроизведение звуко-
вого файла. В качестве значения параметра Режим можно указать именован-
ную константу SND_SYNC (синхронный режим) или SND_ASYNC (асинхронный
режим).
Например, инструкция
PlaySound('ringin.wav',0,SND_ASYNC);

активизирует процесс воспроизведения файла ringin.wav.


194 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Для того чтобы функция PlaySound стала доступной, в программу надо до-
бавить директиву #include <mmsystem.hpp>.
В качестве примера использования функции PlaySound в листинге 5.1 приве-
ден фрагмент программы Будильник — функция обработки сигнала от тай-
мера. Когда наступает время, на которое установлен будильник, на экране
появляется окно с сообщением. Появление окна сопровождается звуком
notify.wav. Так как задан асинхронный режим воспроизведения, то окно по-
является сразу после начала воспроизведения звукового файла.

Листинг 5.1. Использование функции PlaySound

// сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
if ( CompareTime(Now(),AlarmTime) >= 0)
{
Timer1->Enabled = false;
if ( CheckBox1->Checked)
PlaySound("notify.wav",0,SND_ASYNC);

ShowMessage(FormatDateTime(" hh:nn — ", Now() ) + Edit3->Text);

Form1->Show(); // отобразить (развернуть) окно


TrayIcon1->Visible = false;
}
}

Следует обратить внимание на то, что если в имени wav-файла путь не ука-
зан, то функция PlaySound сначала будет искать звуковой файл в текущем
каталоге (в каталоге, из которого запущена программа), затем в каталоге
C:\Windows\Media. Если ни в одном из этих каталогов нужного файла нет, то
будет воспроизведен так называемый "стандартный звук" (задается в на-
стройках Windows). Программист может запретить воспроизведение стан-
дартного звука. Для этого в качестве параметра Режим надо указать константу
SND_NODEFAULT.

Êîìïîíåíò MediaPlayer
Компонент MediaPlayer обеспечивает воспроизведение звуковых файлов раз-
личных форматов (wav, mid, mp3), компакт-дисков, сопровождаемых звуком
анимации и видеороликов (avi).
Ãëàâà 5. Ìóëüòèìåäèà 195

Рис. 5.1. Значок компонента MediaPlayer

Значок компонента MediaPlayer находится на вкладке System (рис. 5.1).


Внешне компонент MediaPlayer представляет собой группу кнопок (рис. 5.2),
подобных тем, которые можно видеть на аудио- или видеоплеере. Назначение
этих кнопок пояснено в табл. 5.1. Свойства компонента MediaPlayer, доступ-
ные во время разработки формы, приведены в табл. 5.2.

Рис. 5.2. Компонент MediaPlayer

Òàáëèöà 5.1. Êíîïêè êîìïîíåíòà MediaPlayer

Кнопка Обозначение Действие

Воспроизведение btPlay Воспроизведение звука или видео

Пауза btPause Приостановка воспроизведения

Стоп btStop Остановка воспроизведения

Следующий btNext Переход к следующему кадру

Предыдущий btPrev Переход к предыдущему кадру

Переход к следующему звуковому фраг-


Шаг btStep менту, например, к следующему треку
(композиции) на CD
Переход к предыдущему звуковому
Назад btBack фрагменту, например, к предыдущей
песне на CD

Запись btRecord Активизирует процесс записи

Открыть btEject Открывает CD-дисковод компьютера


196 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 5.2. Ñâîéñòâà êîìïîíåíòà MediaPlayer

Свойство Описание
Name Имя компонента. Используется для доступа к свойствам ком-
понента и управления работой плеера
DeviceType Тип устройства. Определяет конкретное устройство, которое
представляет собой компонент MediaPlayer.
Тип устройства задается именованной константой:
dtAutoSelect — тип устройства определяется автоматиче-
ски по расширению файла;
dtVaweAudio — проигрыватель звука;
dtAVIVideo — видеопроигрыватель;
dtCDAudio — CD-проигрыватель
FileName Имя файла, в котором находится воспроизводимый звуковой
фрагмент или видеоролик
AutoOpen Признак автоматической загрузки сразу после запуска про-
граммы, файла видеоролика или звукового фрагмента
Display Определяет компонент, поверхность которого используется в
качестве экрана для воспроизведения видеоролика (обычно в
качестве экрана для отображения видео используют компо-
нент Panel)
VisibleButtons Составное свойство. Определяет видимые кнопки компонента

Помимо свойств, доступных в процессе разработки (эти свойства отобража-


ются в окне Object Inspector), у компонента MediaPlayer есть и другие, дос-
тупные только во время работы программы свойства (табл. 5.3). Они позво-
ляют получить информацию о состоянии медиаплеера, воспроизводимом
файле или треке CD. Следует обратить внимание, что значения свойств, со-
держащих информацию о длительности, могут быть представлены в различ-
ных форматах. Наиболее универсальным форматом является формат
tfMilliseconds, в котором длительность выражается в миллисекундах. Не-
которые устройства поддерживают несколько форматов. Например, если
MediaPlayer используется для воспроизведения CD, то информация о вос-
производимом треке может быть представлена в формате tfTMSF (Track,
Minute, Second, Frame — трек, минута, секунда, кадр). Для преобразования
миллисекунд в минуты и секунды надо воспользоваться известными соотно-
шениями. Если значение свойства представлено в формате tfTMSF, то для
преобразования можно использовать функции MCI_TMSF_TRACK, MCI_TMSF_
SECOND и MCI_TMSF_MINUTE.
Ãëàâà 5. Ìóëüòèìåäèà 197

Òàáëèöà 5.3. Ñâîéñòâà êîìïîíåíòà MediaPlayer,


äîñòóïíûå âî âðåìÿ ðàáîòû ïðîãðàììû

Свойство Описание
Length Длина (время, необходимое для воспроизведения) открытого
файла (например, wav или avi) или всех треков Audio CD
Tracks Количество треков на открытом устройстве (количество компози-
ций на Audio CD)
TrackLength Длина (длительность) треков CD. Свойство представляет собой
массив, каждый элемент которого содержит информацию о длине
трека (времени воспроизведения)
Position Позиция (время от начала) в процессе воспроизведения трека
TimeFormat Формат представления значений свойств Length, TrackLength
и Position. Наиболее универсальным является формат
tfMilliseconds. Если медиаплеер представляет собой CD-про-
игрыватель, то удобно использовать формат tfTMSF
Mode Состояние устройства воспроизведения. Устройство может быть
в состоянии воспроизведения (mpPlaying). Процесс воспроизве-
дения может быть остановлен (mpStopped) или приостановлен
(mpPaused). Устройство может быть не готово к работе
(mpNotReady) или в устройстве (CD-дисководе) может отсутство-
вать носитель (mpOpen)
Display Экран — поверхность, на которой отображается клип. Если зна-
чение свойства не задано, то клип отображается в отдельном,
создаваемом во время работы программы, окне
DisplayRect Размер и положение области отображения клипа на поверхности
экрана

Компонент MediaPlayer предоставляет методы (табл. 5.4), используя кото-


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

Òàáëèöà 5.4. Ìåòîäû êîìïîíåíòà MediaPlayer

Метод Действие
Play Активизирует процесс воспроизведения. Действие метода аналогично
щелчку на кнопке Play
Stop Останавливает процесс воспроизведения
Pause Приостанавливает процесс воспроизведения
Next Переход к следующему треку (например, к следующей композиции на
Audio CD)
198 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 5.4 (îêîí÷àíèå)

Метод Действие

Previous Переход к предыдущему треку (например, к следующей композиции


на Audio CD)
Step Переход к следующему кадру
Back Переход к предыдущему кадру

Ïðîñòîé MP3-ïëååð
Как было сказано ранее, компонент MediaPlayer обеспечивает воспроизве-
дение звуковых файлов, в том числе и mp3-формата. Следующий пример по-
казывает, как на основе компонента MediaPlayer можно создать mp3 плеер.
Форма программы приведена на рис. 5.3, значения свойств компонентов —
в табл. 5.5. Следует обратить внимание, что кнопка Eject компонента
MediaPlayer не используется, а ее функцию выполняет кнопка BitBtn. Это
объясняется тем, что кнопками управляет сам компонент и кнопка Eject дос-
тупна только тогда, когда компонент MediaPlayer используется для воспро-
изведения CD (значение свойства DeviceType равно dtCDAudio). Во время
работы программы в поле компонента Label2 отображается длительность —
время, необходимое для воспроизведения выбранного файла, а в поле компо-
нента Label1 — время, прошедшее от момента начала воспроизведения. Щел-
чок на кнопке BitBtn1 открывает стандартное окно Выбор папки. Текст
программы приведен в листинге 5.2. Объявление функций PlayList и
TrackInfo, а также переменных SoundPath, min и sec надо поместить в h-
файл.

Рис. 5.3. Форма программы Простой mp3 плеер


Ãëàâà 5. Ìóëüòèìåäèà 199

Òàáëèöà 5.5. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


MediaPlayer1 DeviceType dtAutoSelect
VisibleButtons.btPause false

VisibleButtons.btStep false

VisibleButtons.btBack false

VisibleButtons.btRecord false

VisibleButtons.btject false

ColoredButtons.btPlay false

ColoredButtons.btStop false

ColoredButtons.btNext false

ColoredButtons.btPrev false
BitBtn1 Glyph

Hint Выбор папки


ShowHint true

Листинг 5.2. Простой mp3-плеер

#include "FileCtrl.hpp" // для доступа к TSearchRec

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)


{
PlayList("");
}

// формирует список mp3-файлов, находящихся в указанном каталоге


void __fastcall TForm1::PlayList(AnsiString path)
{
TSearchRec SearchRec; // структура SearchRec содержит информацию
// о файле, удовлетворяющем условию поиска

ListBox1->Clear();
// сформировать список mp3-файлов
if ( FindFirst(path + "*.mp3", faAnyFile, SearchRec) != 0 )
return;
200 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

// в каталоге есть файл с расширением mp3


// добавим имя этого файла в список
ListBox1->Items->Add(SearchRec.Name);

MediaPlayer1->FileName = SoundPath + ListBox1->Items->Strings[0];


MediaPlayer1->Open();
TrackInfo();

// пока в каталоге есть другие файлы с расширением wav


while (FindNext(SearchRec) == 0)
ListBox1->Items->Add(SearchRec.Name);

ListBox1->ItemIndex = 0;
}

// сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
AnsiString st;

if ( MediaPlayer1->Position == MediaPlayer1->Length )
// воспроизведение текущей композиции не завершено
{
Timer1->Enabled = false;
return;
}

// индикация времени воспроизведения


if ( sec < 59 )
sec++;
else {
sec = 0;
min++;
}

st = IntToStr(min) + ".";
if ( sec < 10)
st = st + "0" + IntToStr(sec);
else
st = st + IntToStr(sec);

Label1->Caption = st;
}
Ãëàâà 5. Ìóëüòèìåäèà 201

// щелчок на кнопке компонента MediaPlayer


void __fastcall TForm1::MediaPlayer1Click(TObject *Sender, TMPBtnType
Button,
bool &DoDefault)
{
switch ( Button ) {
case btPlay: // кнопка Play
min = 0;
sec = 0;
Timer1->Enabled = true;
break;

case btStop: // кнопка Stop


Timer1->Enabled = false;
break;

case btNext: // кнопка Next


if (ListBox1->ItemIndex < ListBox1->Items->Count-1)
{

ListBox1->ItemIndex += 1;
MediaPlayer1->FileName = SoundPath +
ListBox1->Items->Strings[ListBox1->ItemIndex];
MediaPlayer1->Open();
TrackInfo();
Timer1->Enabled = false;
}
break;
case btPrev: // кнопка Prev
if (ListBox1->ItemIndex > 0 )
{

ListBox1->ItemIndex -= 1;
MediaPlayer1->FileName = SoundPath +
ListBox1->Items->Strings [ListBox1->ItemIndex];
MediaPlayer1->Open();
TrackInfo();
Timer1->Enabled = false;
}
break;
}
}
202 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

// выводит информацию о текущем треке


void __fastcall TForm1::TrackInfo()
{
long int TrackLength; // длина трека в миллисекундах
int min,sec; // длина трека: минут, секунд
AnsiString st;

TrackLength = MediaPlayer1->TrackLength[1]/1000;
min = TrackLength / 60;
sec = TrackLength % 60;

st= IntToStr(min) + ".";


if (sec < 10)
st = st + "0" + IntToStr(sec);
else
st = st + IntToStr(sec);

Label2->Caption = st;
Label1->Caption = "0.00";
}

// щелчок на имени файла (композиции)


void __fastcall TForm1::ListBox1Click(TObject *Sender)
{
Timer1->Enabled = false;
MediaPlayer1->Stop(); // остановить воспроизведение текущей композиции

MediaPlayer1->FileName = SoundPath +
ListBox1->Items->Strings [ListBox1->ItemIndex];
MediaPlayer1->Open();
TrackInfo();
}

// щелчок на кнопке Выбор папки


void __fastcall TForm1::BitBtn1Click(TObject *Sender)
{
if ( SelectDirectory("Укажите каталог, в котором находятся
mp3-файлы", "",SoundPath) )
{
SoundPath = SoundPath +"\\";
PlayList(SoundPath);
}
}
Ãëàâà 5. Ìóëüòèìåäèà 203

Ïðîèãðûâàòåëü CD
Следующий пример показывает, как на основе компонента MediaPlayer
можно создать проигрыватель компакт-дисков. Форма программы приведена
на рис. 5.4, значения свойств компонентов — в табл. 5.6. Следует обратить
внимание: помимо компонентов, которые показаны на рисунке, в форме есть
компонент MediaPlayer и две кнопки SpeedButton. Эти компоненты нахо-
дятся за границей формы (чтобы их увидеть, надо увеличить размер формы).
Сделано это потому, что управление плеером осуществляется кнопками
SpeedButton1—SpeedButton3, а не кнопками компонента MdiaPlayer. Кнопки
SpeedButton4 и SpeedButton5 тоже не используются в процессе работы про-
граммы, они хранят картинки "Play" и "Stop". Во время работы программы
(в момент активизации процесса воспроизведения) битовый образ кнопки
SpeedButton5 копируется в битовый образ кнопки SpeedButton2 (в резуль-
тате на кнопке появляется значок "Stop"). Если процесс воспроизведения ак-
тивен, то в момент щелчка на кнопке SpeedButton2 битовый образ кнопки
SpeedButton4 копируется в битовый образ кнопки SpeedButton2 (в резуль-
тате на кнопке появляется значок "Play").

Рис. 5.4. Форма программы Compact Disc Player

Òàáëèöà 5.6. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


MediaPlayer DeviceType dtCDAudio
SpeedButton1 NumGlyphs 2
Glyph
Flat true
Enabled false
SpeedButton2 NumGlyphs 2
Glyph
Flat true
Enabled false
204 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 5.6 (îêîí÷àíèå)

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


SpeedButton3 NumGlyphs 2
Glyph
Flat true
Enabled false
SpeedButton4 NumGlyphs 2
Glyph
SpeedButton5 NumGlyphs 2
Glyph
Timer1 Interval 500

Компонент Timer используется для организации цикла опроса состояния ме-


диаплеера. Во время воспроизведения CD функция обработки события Timer
(которое генерирует таймер) выводит на индикатор (в поля компонентов
Label1 и Label2) номер воспроизводимого трека, его длительность и время
воспроизведения.
Вид окна программы сразу после ее запуска (когда в CD-дисководе находится
Audio CD) приведен на рис. 5.5, а и б. Если в дисководе нет диска или диск
не звуковой, то на индикаторе отображается сообщение Вставьте Audio CD.
Щелчок на кнопке Play (SpeedButton2) активизирует процесс воспроизведе-
ния. Во время воспроизведения на индикаторе отражается номер воспроизво-
димого трека, его длительность, а также время от начала воспроизведения
(рис. 5.6).

а б
Рис. 5.5. В начале работы на индикаторе выводится информация
о времени воспроизведения CD (а)
или сообщение о необходимости вставить в дисковод Audio CD (б)

Текст программы приведен в листинге 5.3. Следует обратить внимание на


событие Notify, которое может генерировать MediaPlayer. Это событие воз-
Ãëàâà 5. Ìóëüòèìåäèà 205

никает в момент изменения состояния плеера (например, в момент активиза-


ции процесса воспроизведения) при условии, что значение свойства Notify
равно true. В рассматриваемой программе событие Notify используется для
обнаружения факта открытия CD-дисковода пользователем.

Рис. 5.6. Во время воспроизведения на индикаторе отображается информация


о воспроизводимом треке

Листинг 5.3. Проигрыватель компакт-дисков

// эти макросы обеспечивают перевод интервала времени,


// выраженного в миллисекундах, в минуты и секунды
#define MINUTE(ms) ((ms/1000)/60)
#define SECOND(ms) ((ms/1000)%60)

// выводит в поле информацию о текущем треке


void __fastcall TForm1::TrackInfo()
{
int ms; // время звучания трека, мсек
AnsiString st;

Track = MCI_TMSF_TRACK(MediaPlayer1->Position);

MediaPlayer1->TimeFormat = tfMilliseconds;
ms = MediaPlayer1->TrackLength[Track];
MediaPlayer1->TimeFormat = tfTMSF;

st = IntToStr(SECOND(ms));
if ( st.Length() == 1)
st = "0" + st;

st = "Трек "+ IntToStr(Track) +


". Длительность "+ IntToStr(MINUTE(ms)) + ":" + st;
Label1->Caption = st;
}

void __fastcall TForm1::FormActivate(TObject *Sender)


{
MediaPlayer1->Open();
206 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

/* Проверить, есть ли в CD-ROM Audio CD.


Если в дисководе есть диск, то свойство
Mode = mpStopped. Если диска нет,
или дисковод открыт, то Mode = mpOpen.
Тип диска можно определить по количеству
треков. Если диск — CD-ROM, то на нем один
трек, на аудиодиске – кол-во треков больше 1 */
if ((MediaPlayer1->Mode == mpStopped) &&
(MediaPlayer1->Tracks > 1) )

MediaPlayer1->Notify = true;
else Timer1->Enabled = true;
}
// сигнал от MediaPlayer
void __fastcall TForm1::MediaPlayer1Notify(TObject *Sender)
{
if ( MediaPlayer1->Mode == mpOpen) // пользователь открыл дисковод
{
SpeedButton2->Tag = 0;
SpeedButton2->Enabled = false;
SpeedButton2->Enabled = false;
Timer1->Enabled = true;
Label2->Caption = "00:00";
}
MediaPlayer1->Notify = true;
}

// щелчок на кнопке "к предыдущему треку"


void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
MediaPlayer1->Previous(); // в начало текущего трека
MediaPlayer1->Previous(); // в начало предыдущего трека
if ( MCI_TMSF_TRACK(MediaPlayer1->Position) == 1)
SpeedButton1->Enabled = false;
if (! SpeedButton3->Enabled)
SpeedButton3->Enabled = true;
}

// щелчок на кнопке Play/Stop


void __fastcall TForm1::SpeedButton2Click(TObject *Sender)
{
int trk; // номер трека
Ãëàâà 5. Ìóëüòèìåäèà 207

if (SpeedButton2->Tag == 0)
{
// щелчок на кнопке Play
MediaPlayer1->Play();
MediaPlayer1->TimeFormat = tfTMSF;
trk = MCI_TMSF_TRACK(MediaPlayer1->Position);
if (trk > 1)
SpeedButton1->Enabled = true;
if (trk < MediaPlayer1->Tracks)
SpeedButton3->Enabled = true;

MediaPlayer1->Notify = false;

Timer1->Enabled = true;
SpeedButton2->Tag = 1;
// на кнопке SpeedButton5 картинка Stop
SpeedButton2->Glyph = SpeedButton5->Glyph;
}
else
{ // щелчок на кнопке Stop
SpeedButton1->Enabled = false;
SpeedButton3->Enabled = false;
MediaPlayer1->Notify = true;
MediaPlayer1->Stop();
Timer1->Enabled = false;
SpeedButton2->Tag = 0;
// на кнопке SpeedButton4 картинка Play
SpeedButton2->Glyph = SpeedButton4->Glyph;
}
}

// щелчок на кнопке "к следующему треку"


void __fastcall TForm1::SpeedButton3Click(TObject *Sender)
{
MediaPlayer1->Next();
// если перешли к последнему треку, то кнопку
// Next сделать недоступной
if (MCI_TMSF_TRACK(MediaPlayer1->Position) == MediaPlayer1->Tracks)
SpeedButton3->Enabled = false;
if ( ! SpeedButton1->Enabled)
SpeedButton1->Enabled = true;
}
208 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

// сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
int trk; // трек
long int ms; // время звучания в миллисекундах
int min, sec; // время
AnsiString st;

switch (MediaPlayer1->Mode)
{
case mpPlaying: // режим воспроизведения

// Вывести номер трека и время воспроизведения


trk = MCI_TMSF_TRACK(MediaPlayer1->Position);
min = MCI_TMSF_MINUTE(MediaPlayer1->Position);
sec = MCI_TMSF_SECOND(MediaPlayer1->Position);
st.printf("%d:%.2d",min,sec);
Label2->Caption = st;

if ( trk != Track)
{
// начало воспроизведения нового трека
Track = trk;
TrackInfo();
if (Track == 2)
SpeedButton2->Enabled = true;
if (Track == MediaPlayer1->Tracks)
SpeedButton3->Enabled = false;
}
break;

case mpStopped:
// если дисковод открыт или в нем нет
// Audio CD, то Mode = mpOpen.
if (MediaPlayer1->Tracks > 1)
{
// в дисководе Audio CD
// вывести информацию о диске
Timer1->Enabled = false;
MediaPlayer1->Open();
MediaPlayer1->Notify = true;

trk = MCI_TMSF_TRACK(MediaPlayer1->Tracks);
Ãëàâà 5. Ìóëüòèìåäèà 209

MediaPlayer1->TimeFormat = tfMilliseconds;
ms = MediaPlayer1->Length;
ms = ms / 1000;

min = ms / 60;
sec = ms % 60;

SpeedButton2->Enabled = true; // кнопка Play

Label1->
Caption.printf("Треков: %d. Время звучания: %d:%.2d",
trk,min,sec);
Label1->Visible = true;
}
break;

case mpOpen: // дисковод открыт или в дисководе нет диска


Label1->Caption = "Вставьте Audio CD";
Label1->Visible = ! Label1->Visible;
}

// завершение работы программы


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
MediaPlayer1->Stop();
}

Âîñïðîèçâåäåíèå MIDI-ìóçûêè
Электронная или MIDI-музыка (Musical Instrument Digital Interface) широко
используется в компьютерных играх. Это объясняется тем, что при равном
времени звучания размер midi-файла существенно меньше соответствующего
wav- или mp3-файла.
В следующей программе (ее форма приведена на рис. 5.7) компонент
MediaPlayer обеспечивает воспроизведение MIDI-музыки. Программа Охота
представляет собой простейшую игру. В ее окне (после щелчка на кнопке
OK) перемещается утка (рис. 5.8). Игрок должен выстрелить в утку — уста-
новить указатель мыши на ее изображение и нажать левую кнопку мыши.
Игра заканчивается после 10 выстрелов.
Текст программы приведен в листинге 5.4. Следует обратить внимание: объ-
явление функций DrawDuck, EraseDuck, которые рисуют и стирают утку, а
210 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

также битовых образов duck и bg следует поместить в секцию private объ-


явления класса формы.

Рис. 5.7. Форма программы Охота

Рис. 5.8. Окно программы Охота

Листинг 5.4. Игра Охота (воспроизведение MIDI)

TPoint p; // положение утки


int n; // количество выстрелов
int m; // количество попаданий

__fastcall TForm1::TForm1(TComponent* Owner)


: TForm(Owner)
{
MediaPlayer1->FileName = "pulp_fiction.mid";
MediaPlayer1->Open(); // открыть midi-файл
MediaPlayer1->Play(); // воспроизведение
Ãëàâà 5. Ìóëüòèìåäèà 211

// загрузить фоновый рисунок


bg = new Graphics::TBitmap;
bg->LoadFromFile("bg.bmp");

// загрузить изображение объекта (утки)


duck = new Graphics::TBitmap; // создать объект TBitmap
duck->LoadFromFile("duck_1.bmp"); // загрузить картинку
duck->Transparent = true;

// буфер для хранения фрагмента фона


bb = new Graphics::TBitmap;
bb->Width = duck->Width;
bb->Height = duck->Height;

Timer1->Interval = 500;
}

// нарисовать утку
void __fastcall TForm1::DrawDuck(TPoint p)
{
TRect dest, source;

dest = Rect(0,0,bb->Width, bb->Height );


source = Bounds(p.x,p.y, bb->Width, bb->Height );

// сохранить область фона, в которую будет выведено изображение


bb->Canvas->CopyRect(dest,Form1->Canvas,source);

// нарисовать утку
Form1->Canvas->Draw(p.x,p.y,duck);
}

// стереть утку
void __fastcall TForm1::EraseDuck(TPoint p)
{
// восстановить "испорченную" область фона
Form1->Canvas->Draw(p.x,p.y,bb);
}

// сигнал от таймера
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
// стереть объект
EraseDuck(p);
212 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

// сгенерировать координаты
p.x = random(ClientWidth);
if (p.x > duck->Width)
p.x -= duck->Width;

p.y = random(ClientHeight);
if (p.y > duck->Height)
p.y -= duck->Height;

// нарисовать на новом месте


DrawDuck(p);
}

// щелчок на кнопке OK
void __fastcall TForm1::Button1Click(TObject *Sender)
{
Label1->Visible = false; // скрыть сообщение
Button1->Visible = false; // скрыть кнопку
n =0;
m=0;
p.x = Form1->ClientWidth /2;
p.y = 90;

Form1->Canvas->Draw(0,0,bg); // нарисовать фон


Timer1->Enabled = true; // запустить таймер
}

// нажатие кнопки мыши


void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton
Button,
TShiftState Shift, int X, int Y)
{
if ((X > p.x)&& (X < p.x + duck->Width) &&
(Y > p.y) && (Y < p.y + duck->Height))
{
// попал!
m++;
EraseDuck(p);
}

n++;
if (n == 10 ) {

Timer1->Enabled = false;
Ãëàâà 5. Ìóëüòèìåäèà 213

ShowMessage("Выстрелов:" + IntToStr(n) +
"\nПопаданий:" + IntToStr(m));
EraseDuck(p);
Label1->Visible = true;
Button1->Visible = true;
}
}

// сигнал от медиаплеера
void __fastcall TForm1::MediaPlayer1Notify(TObject *Sender)
{
// Если значение свойства Notify равно true
// (метод Play присваивает свойству Notify значение true),
// то в момент окончания воспроизведения возникает событие Notyfy
MediaPlayer1->Play(); // проиграть мелодию еще раз
}

// завершение работы программы


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
MediaPlayer1->Close();
}

void __fastcall TForm1::FormPaint(TObject *Sender)


{
if (Timer1->Enabled) {
Form1->Canvas->Draw(0,0,bg);
DrawDuck(p);
}
}

Музыка начинает звучать сразу после запуска программы: конструктор фор-


мы загружает midi-файл и активизирует процесс его воспроизведения. Следу-
ет обратить внимание, что мелодия воспроизводится "по кругу" до тех пор,
пока игрок не закроет окно программы. Процесс повторного воспроизведения
музыки активизирует функция обработки события Notify медиаплеера. Это
событие возникает всякий раз, когда по какой-либо причине состояние плее-
ра меняется, в том числе и в момент завершения воспроизведения файла.
Также необходимо обратить внимание на функцию обработки события Close
формы. Она проверяет состояние плеера, и если плеер воспроизводит музы-
ку, то останавливает его.
214 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Ïðîñìîòð âèäåîðîëèêîâ
Компонент MediaPlayer позволяет просматривать видеоролики и сопровож-
даемую звуком анимацию. В качестве примера использования компонента
для решения этой задачи рассмотрим программу Video Player (рис. 5.9),
с помощью которой можно просмотреть небольшой ролик или анимацию.
Форма программы приведена на рис. 5.10, значения свойств компонентов —
в табл. 5.7.

Рис. 5.9. Окно программы Video Player

Рис. 5.10. Форма программы Video Player


Ãëàâà 5. Ìóëüòèìåäèà 215

Òàáëèöà 5.7. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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

MediaPlayer DeviceType dtAutoSelect

Visible false

SpeedButton1 NumGlyphs 4

Glyph

Flat true

Enabled true

SpeedButton2 NumGlyphs 4

Glyph

Flat true

Enabled false

GroupIndex 1

AllowAllUp true

Timer1 Interval 1000

Enabled false

Компонент OpenDialog1 обеспечивает отображение стандартного диалогово-


го окна Открыть файл для выбора файла. Окно Открыть файл становится
доступным во время работы программы в результате щелчка на кнопке Eject
(SpeedButton1). Следует обратить внимание, что для управления процессом
воспроизведения кнопки компонента MediaPlayer1 не используются, поэто-
му свойству Visible компонента MediaPlayer присвоено значение false.
Также необходимо обратить внимание на свойство GroupIndex кнопки
SpeedButon1. Его значение равно единице, поэтому после щелчка кнопка ос-
тается в зафиксированном (нажатом) состоянии и на ее поверхности появля-
ется значок "Stop" (значение свойства NumGlyhts равно 4, это значит, что в
битовом образе есть картинка для нажатого состояния). Компонент Timer
обеспечивает обновление информации на индикаторе: функция обработки
сигнала от таймера (события Timer) выводит в поле Label2 информацию о
времени воспроизведения клипа.
Текст программы приведен в листинге 5.5.
216 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Листинг 5.5. Видеоплеер

/*
Простой видеоплеер. Демонстрирует использование:
— компонента MediaPlayer для воспроизведения
видеороликов (формат avi, mpg);
— компонента SpeedButton.

Замечание. Если окно программы, когда воспроизведение клипа закончено,


перекрыть другим окном, то кадр будет испорчен. Чтобы этого не было,
надо написать функцию обработки события Paint для формы.
*/

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;

// эти макросы обеспечивают перевод интервала времени,


// выраженного в миллисекундах, в минуты и секунды
#define MINUTE(ms) ((ms/1000)/60)
#define SECOND(ms) ((ms/1000)%60)

__fastcall TForm1::TForm1(TComponent* Owner)


: TForm(Owner)
{
MediaPlayer1->Display = Form1;
}

// возвращает размер кадра


void __fastcall GetFrameSize(AnsiString f, int *w, int *h)
{
if ( f.Pos(".avi") == 0 )
{
// пользователь выбрал mpg-файл
*w = 352;
Ãëàâà 5. Ìóëüòèìåäèà 217

*h = 240;
return;
}

// *** Пользователь выбрал avi-файл ***


// В заголовке avi-файла есть информация о размере кадра
struct {
char RIFF[4]; // строка RIFF
long int nu_1[5]; // не используется
char AVIH[4]; // строка AVIH
long int nu_2[9]; // не используется
long int w; // ширина кадра
long int h; // высота кадра
} header;

TFileStream *fs; // поток для чтения заголовка файла

/* операторы объявления потока и его создания


можно объединить: TFileStream *fs = new TFileStream(f,fmOpenRead); */

fs = new TFileStream(f,fmOpenRead); // открыть поток для чтения


fs->Read(&header, sizeof(header)); // прочитать заголовок файла
*w = header.w;
*h = header.h;
delete fs;
}

// щелчок на кнопке Eject (выбор видеоклипа)


void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
int fw, fh; // размер кадра клипа
int top,left; // левый верхний угол экрана
int sw, sh; // размер экрана (ширина, высота)

int mw, mh; // максимально возможный размер экрана


// (определяется текущим размером формы)

float kw, kh; // коэф-ты масштабирования кадра по ширине и высоте


float k; // коэффициент масштабирования кадра

OpenDialog1->Title = "Выбор клипа";


OpenDialog1->InitialDir = "";
218 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

OpenDialog1->Filter =
"Все форматы|*.avi;*.mpg;*.mpeg|"
"AVI|*.avi|MPG|*.mpg|MGEG|*.mpeg";

if ( ! OpenDialog1->Execute() )
return; // пользователь нажал кнопку Отмена

/* При попытке открыть файл клипа, который уже открыт,


возникает ошибка. */

if ( MediaPlayer1->FileName == OpenDialog1->FileName )
return;

/* Пользователь выбрал клип. Зададим размер и положение "экрана",


на котором будет выведен клип. Для этого надо знать размер
кадров клипа. */

//Form1->Caption = "Video Player — " + OpenDialog1->FileName;


GetFrameSize(OpenDialog1->FileName,&fw, &fh);// получить размер кадра

// вычислим максимально возможный размер кадра


mw = Form1->ClientWidth;
mh = Form1->Panel1->Top-10;

if ( fw < mw)
kw = 1; // кадр по ширине меньше размера экрана
else kw = (float) mw / fw;

if ( fh < mh)
kh = 1; // кадр по высоте меньше размера экрана
else kh = (float) mh / fh;

// масштабирование должно быть пропорциональным


if ( kw < kh)
k = kw;
else k = kh;

// здесь масштаб определен


sw = fw * k; // ширина экрана
sh = fh * k; // высота экрана

left = (Form1->ClientWidth — sw) / 2;


top = (Panel1->Top — sh) / 2;
Ãëàâà 5. Ìóëüòèìåäèà 219

MediaPlayer1->FileName = OpenDialog1->FileName;

MediaPlayer1->Open();
MediaPlayer1->DisplayRect = Rect(left,top,sw,sh);
/* если размер кадра выбранного клипа меньше размера
кадра предыдущего клипа, то экран (область формы)
надо очистить */
Form1->Canvas->FillRect(Rect(0,0,ClientWidth,Panel1->Top));

SpeedButton2->Enabled = True; // кнопка Play теперь доступна

// вывести информацию о времени воспроизведения


MediaPlayer1->TimeFormat = tfMilliseconds;
int ms = MediaPlayer1->Length;
AnsiString st = IntToStr(SECOND(ms));
if ( st.Length() == 1)
st = "0" + st;
st = IntToStr(MINUTE(ms)) + ":" + st;
Label1->Caption = st;
Label2->Caption = "0:00";

// активизируем процесс воспроизведения


SpeedButton2->Down = true;
SpeedButton2->Hint = "Стоп";
SpeedButton2->Tag = 1;
SpeedButton1->Enabled = false; // кнопка Eject недоступна
MediaPlayer1->Play();
Timer1->Enabled = true;
}

// щелчок на кнопке Play/Stop (воспроизведение/стоп)


void __fastcall TForm1::SpeedButton2Click(TObject *Sender)
{
if (SpeedButton2->Tag == 0)
{
// нажата кнопка Play
SpeedButton2->Down = true;;
SpeedButton2->Hint = "Стоп";
SpeedButton2->Tag = 1;
SpeedButton1->Enabled = false; // кнопка Eject недоступна
MediaPlayer1->Play();
Timer1->Enabled = true;
}
220 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

else // нажата кнопка Stop


{
MediaPlayer1->Stop();
SpeedButton2->Down = false;
SpeedButton2->Hint = "Play";
SpeedButton2->Tag = 0;
SpeedButton1->Enabled = true; // кнопка Eject доступна
Timer1->Enabled = false;
}
}

// сигнал от плеера
void __fastcall TForm1::MediaPlayer1Notify(TObject *Sender)
{
if ( ( MediaPlayer1->Mode == mpStopped ) && ( SpeedButton2->Tag ==
1))
{
Timer1->Enabled = false;
SpeedButton2->Down = false;
SpeedButton2->Hint = "Play";
SpeedButton2->Tag = 0;
SpeedButton1->Enabled = true; // сделать доступной кнопку Eject
}
}

/* Процедура обработки события Pain обеспечивает


отображение (перерисовку) первого кадра
при появлении окна, например, после того,
как пользователь отодвинет другое окно, перекрывающее
окно Video Player. */
void __fastcall TForm1::FormPaint(TObject *Sender)
{
if ( MediaPlayer1->Mode == mpStopped )
{
MediaPlayer1->Position = 1;
MediaPlayer1->Position = 0;
}
}

// завершение работы программы


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
MediaPlayer1->Close();
}
Ãëàâà 5. Ìóëüòèìåäèà 221

void __fastcall TForm1::Timer1Timer(TObject *Sender)


{
// вывести информацию о времени воспроизведения
// MediaPlayer1->TimeFormat = tfMilliseconds;
int ms = MediaPlayer1->Position;
AnsiString st = IntToStr(SECOND(ms));
if ( st.Length() == 1)
st = "0" + st;
st = IntToStr(MINUTE(ms)) + ":" + st;
Label2->Caption = st;
}

В качестве экрана, на котором осуществляется воспроизведение видеороли-


ков, используется поверхность формы. Поэтому установить значение свойст-
ва Display компонента MediaPlayer1 во время разработки формы нельзя.
Кроме того, размер экрана должен быть равен или пропорционален размеру
кадров ролика. Значение свойства Display устанавливает функция обработки
события Create для формы, а размер и положение экрана на форме — функ-
ция обработки события Click на кнопке Eject (SpeedButton1). Размер экрана
устанавливается максимально возможным и таким, чтобы ролик воспроизво-
дился без искажения (высота и ширина экрана пропорциональны высоте и
ширине кадров). Размер кадров ролика возвращает функция GetFrameSize,
которая извлекает нужную информацию из заголовка файла.

Êîìïîíåíò Animate
Компонент Animate, его значок находится на вкладке Win32 (рис. 5.11), по-
зволяет воспроизвести простую, не сопровождаемую звуком анимацию, кад-
ры которой находятся в avi-файле.
Свойства компонента Animate перечислены в табл. 5.8.
Нужно еще раз обратить внимание на то, что компонент Animate предназна-
чен для воспроизведения avi-файлов, которые содержат только анимацию.
При попытке открыть файл, в котором находится анимация, сопровождаемая
звуком, возникает исключение.

Рис. 5.11. Значок компонента Animate


222 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 5.8. Ñâîéñòâà êîìïîíåíòà Animate

Свойство Описание
FileName Имя avi-файла, в котором находится анимация
FrameWidth Ширина кадра
FrameHeight Высота кадра
FrameCount Количество кадров анимации
AutoSize Признак автоматического изменения размера компонента в соот-
ветствии с размером кадров анимации
Center Признак центрирования кадров анимации в поле компонента. Ес-
ли значение свойства равно true и размер кадров меньше раз-
мера компонента (при условии, что значение свойства AutoSize
false), то кадры анимации располагаются в центре поля компо-
нента
StartFrame Номер кадра, с которого начинается отображение анимации
StopFrame Номер кадра, на котором заканчивается отображение анимации
Active Признак активности процесса отображения анимации
Color Цвет фона компонента (цвет "экрана"), на котором воспроизво-
дится анимация
Transparent Режим использования "прозрачного" цвета при отображении ани-
мации
Repetitions Количество повторов отображения анимации. Если значение
свойства равно нулю, то анимация воспроизводится непрерывно
CommonAVI Определяет стандартную анимацию, которая отображается в по-
ле компонента:
aviCopy — копирование файла;
aviDeleteFile — удаление файла;
aviRecicleFile — перемещение файла в корзину

Следующая программа (ее окно приведено на рис. 5.12, а текст — в листин-


ге 5.6) демонстрирует использование компонента Animate для отображения
анимации. В момент появления окна на экране в поле компонента Animate
отображается последний кадр анимации — изображение Дельфийского хра-
ма. Щелчок на кнопке Play активизирует процесс отображения анимации.
Непосредственно воспроизведение анимации инициирует метод Play, пара-
метры которого задают начальный и конечный кадры фрагмента анимации,
который надо воспроизвести, и число повторов отображения анимации. На-
чальный и конечный кадры, а также количество повторов можно задать, при-
Ãëàâà 5. Ìóëüòèìåäèà 223

своив значения, соответственно, свойствам StartFrame, StopFrame и


Repetitions. В этом случае, чтобы активизировать процесс воспроизведения
анимации, необходимо свойству Activate присвоить значение true.

Рис. 5.12. Отображение анимации в поле компонента Animate

Листинг 5.6. Отображение AVI-анимации

bool loaded = false; // анимация загружена

// конструктор формы
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
/* если файл анимации недоступен или анимация
сопровождается звуком, возникает исключение */
try
{
Animate1->FileName = "delphi.avi";
}
catch (Exception &e)
{
}
Form1->Caption = "Анимация — " + Animate1->FileName;
loaded = true;
Label1->Caption =
" Размер кадра: " +
IntToStr(Animate1->Width) + "x" + IntToStr(Animate1->Height) +
", кадров: " + IntToStr(Animate1->FrameCount);
}

// начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
224 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

{
if ( loaded)
// воспроизвести анимацию один раз с первого по последний кадр
Animate1->Play(1,Animate1->FrameCount,1);
}

// щелчок на кнопке Play


void __fastcall TForm1::Button1Click(TObject *Sender)
{
if ( loaded)
// воспроизвести анимацию один раз с первого по последний кадр
Animate1->Play(1,Animate1->FrameCount,1);
}
ÃËÀÂÀ 6

Áàçû äàííûõ

C++ Builder предоставляет программисту компоненты, используя которые он


может создать программу работы практически c любой базой данных: от
Microsoft Access до Microsoft SQL Server и Oracle.

Áàçà äàííûõ è ÑÓÁÄ


База данных — это файл или совокупность файлов определенной структуры,
в которых находится информация. Программная система, обеспечивающая
работу с базой данных, называется системой управления базой данных
(СУБД). СУБД позволяет создать базу данных, наполнить ее информацией,
решить задачи отображения и поиска данных. Типичным примером СУБД
является Microsoft Access.

Ëîêàëüíûå è óäàëåííûå áàçû äàííûõ


В зависимости от расположения данных и программы, которая обеспечивает
доступ к ним, а также от способа разделения данных между несколькими
пользователями различают локальные и удаленные базы данных.
В локальной базе файлы данных обычно находятся на диске того же компью-
тера, на котором работает программа манипулирования данными. Локальные
базы данных не обеспечивают одновременный доступ к информации не-
скольким пользователям. Несомненным достоинством локальных баз являет-
ся высокая скорость доступа к информации. Microsoft Access — это типичная
локальная база данных.
В удаленных базах данные размещают на отдельном компьютере (сервере).
Программы, обеспечивающие работу с удаленными базами, строят по техно-
226 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

логии "клиент-сервер". Клиент (программа, работающая на компьютере


пользователя) обеспечивает прием команд от пользователя, передачу их сер-
веру, получение и отображение данных. Сервер (программа, работающая на
удаленном компьютере) принимает запросы от клиента, выполняет их и пе-
ресылает данные клиенту. Сервер проектируется так, чтобы обеспечить дос-
туп к данных многим пользователям. Взаимодействие клиента и сервера
осуществляется по сети на основе протокола TCP/IP. В большинстве случаев
в качестве сервера используется сервер баз данных (например, Microsoft SQL
Server, MySQL, Interbase или другой). Таким образом, разработка программы
работы с удаленной базой данных в большинстве случаев сводится к разра-
ботке программы-клиента.

Ñòðóêòóðà áàçû äàííûõ


База данных — это набор однородной, как правило, упорядоченной по неко-
торому критерию информации (или, другими словами, записей).
На практике наиболее широко используются реляционные базы данных. Ре-
ляционная база данных — это совокупность таблиц данных. Например, базу
данных Projects (Проекты) можно представить как совокупность таблиц
Projects (Проекты), Tasks (Задачи) и Resources (Ресурсы), а базу данных
Contacts (Контакты) — одной-единственной таблицей Contacts (Контакты).
Строки таблиц данных называют записями. Записи содержат информацию об
объектах базы данных. Они состоят из полей, которые, в свою очередь, со-
держат информацию о характеристиках объектов. При представлении данных
в табличной форме имена полей указывают в заголовках столбцов, номера
записей — в первом столбце.
В качестве примера на рис. 6.1 приведены таблицы базы данных Projects
(Проекты). Таблица Projects содержит информацию о проектах (идентифика-
тор и название проекта, даты начала и завершения, состояние), таблица
Tasks — о задачах (идентификатор и название задачи, дата, когда работы по
выполнению задачи должны быть начаты, статус задачи, длительность зада-
чи, идентификатор проекта, к которому относится задача, и идентификатор
ресурса, который должен обеспечить выполнение задачи), таблица
Resource — о ресурсах (идентификатор и название).
Обратите внимание: поля ProjID, TaskID и ResID обеспечивают связь между
таблицами. Например, зная имя проекта можно из таблицы Project получить
идентификатор проекта, а затем из таблицы Task — список задач, относящих-
ся к этому проекту. Аналогичным образом, зная идентификатор задачи, мож-
но получить из таблицы Resource имя ресурса, обеспечивающего выполнение
задачи.
Ãëàâà 6. Áàçû äàííûõ 227

Рис. 6.1. Таблицы база данных Projects (Проекты)

Ìåõàíèçìû äîñòóïà ê äàííûì


Существует достаточно много технологий доступа к данным: BDE, ADO,
dbExpress и др.
Технология Borland Database Engine — (BDE) механизм доступа к данным,
основой которой является процессор баз данных, представляющий собой на-
бор динамических библиотек, драйверов и утилит. BDE обеспечивает работу
практически с любой из существующих баз данных. Однако при всех своих
достоинствах технология BDE не лишена недостатков, одним из которых яв-
ляется достаточно трудоемкий процесс развертывания приложений, создан-
ных на ее основе: помимо программы работы с базой данных, на компьютер
пользователя необходимо установить BDE и выполнить его настройку.
Технология ActiveX Data Object (ADO) разработана Microsoft. В настоящее
время именно она наиболее широко используется для доступа к данным. Не-
228 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

сомненным ее достоинством является возможность получения данных из раз-


личных источников. Эта возможность обеспечивается соответствующими
драйверами.
Технология dbExpress — это разработанная Borland технология однонаправ-
ленного (unidirect) доступа к данным, отличительной особенностью которой
является высокая скорость доступа.

Êîìïîíåíòû äîñòóïà ê äàííûì


Компоненты, обеспечивающие доступ к данным, находятся на вкладках
dbGo, dbExpress, InterBase и BDE.
Компоненты вкладки dbGo для доступа к данным используют разработанную
Microsoft технологию ADO. Компоненты dbExpress обеспечивают высоко-
эффективный однонаправленный (unidirectional) доступ к удаленным базам
данных на основе разработанной Borland технологии dbExpress. Компоненты
вкладки InterBase оптимизированы для работаты с базами данных InterBase.
Компоненты вкладки BDE для доступа к данным используют процессор баз
данных Borland Database Engine.
Следует обратить внимание на то, что компоненты доступа к данным напря-
мую с базами данных не взаимодействуют — доступ к данным обеспечивают
соответствующие драйверы. Таким образом, чтобы компонент мог взаимо-
действовать с базой данных, на компьютере должен быть установлен соот-
ветствующий драйвер. На компьютер разработчика драйверы баз данных
устанавливаются в процессе установки среды разработки.

Êîìïîíåíòû îòîáðàæåíèÿ äàííûõ


На вкладке DataControls находятся компоненты, обеспечивающие отображе-
ние данных. Компонент DataGrid используется для отображения данных в
табличной форме. Компоненты DBEdit, DBText обеспечивают отображение
содержимого отдельных полей.

Áàçà äàííûõ Microsoft Access


Microsoft Access является популярной системой ведения баз данных. Это
объясняется сочетанием удобства работы, широкими возможностями, пре-
доставляемыми системой пользователям и разработчикам, возможностью
доступа к данным из офисных приложений.
Процесс разработки программы работы с базой данных Microsoft Access рас-
смотрим на примере. Создадим приложение, обеспечивающее работу с базой
Ãëàâà 6. Áàçû äàííûõ 229

данных Контакты (contacts.mdb). Для доступа к данным будем использовать


технологию ADO.

Перед тем как приступить к непосредственной работе в C++ Builder, необходи-


мо с помощью Microsoft Access создать (например, в папке D:\Database\) файл
базы данных contacts.mdb и поместить в него таблицу Contacts (табл. 6.1).
Также в каталоге D:\Database\ следует создать каталог Images. В этом каталоге
будем хранить иллюстрации.

Òàáëèöà 6.1. Òaáëèöà Contacts áàçû äàííûõ Контакты (contacts.mdb)

Поле Тип Размер Описание

Name Текстовый 50 Имя

Phone Текстовый 30 Телефон

Comment Текстовый 100 Комментарий (дополнительная информация)

Image Текстовый 30 Имя файла иллюстрации (например,


фотографии)

Äîñòóï ê äàííûì
Доступ к данным при использовании технологии ADO обеспечивают компо-
ненты ADOConnection, ADODataSet, ADOTable и ADOQuery, которые находятся на
вкладке dbGo (рис. 6.2).

Рис. 6.2. Компоненты вкладки dbGo обеспечивают доступ к данным

Компонент ADOConnection обеспечивает соединение с базой данных (источ-


ником данных).
Компонент ADODataSet представляет собой данные, полученные от источника
данных в результате выполнения SQL-запроса.
Компонент ADOTable также представляет собой данные, полученные из базы
данных, но, в отличие от компонента ADODataSet, который может быть запол-
230 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

нен информацией из разных таблиц, этот компонент представляет собой дан-


ные, полученные из одной таблицы.
Компонент ADOQuery представляет собой данные, полученные из базы данных
в результате выполнения SQL-команды.
Для связи между данными, в качестве которых может выступать компонент
ADODataSet, ADOTable или ADOQuery, и компонентом, обеспечивающим отобра-
жение данных (например, DBGrid), используется компонент DataSource.
Механизм взаимодействия компонентов, обеспечивающих доступ к данным и
их отображение, показан на рис. 6.3.

Рис. 6.3. Взаимодействие компонентов, обеспечивающих доступ к данным


и их отображение

В форму программы работы с базой данных Контакты надо добавить компо-


ненты ADOConnection, ADODataSet, DataSourse и DBGrid (рис. 6.4). Компоненты
рекомендуется добавлять в том порядке, в котором они перечислены, и сразу
настраивать. Необходимо отметить, что компоненты ADOConnection,
ADODataSet, DataSourse являются невизуальными (в окне программы во время
ее работы не видны). Поэтому их можно поместить в любую область формы.
Компонент ADOConnection (его свойства приведены в табл. 6.2) обеспечивает
соединение с базой данных.
Ãëàâà 6. Áàçû äàííûõ 231

Рис. 6.4. Форма программы работы с базой данных Контакты

Òàáëèöà 6.2. Ñâîéñòâà êîìïîíåíòà ADOConnection

Свойство Описание

ConnectionString Строка соединения. Содержит информацию, необходимую


для подключения к базе данных
LoginPrompt Признак необходимости запроса имени и пароля пользова-
теля в момент подключения к базе данных. Если значение
свойства равно false, то окно Login в момент подключения
к базе данных не отображается
Mode Режим соединения. Соединение с базой данных может быть
открыто для чтения (cmRead), записи (cmWrite), чте-
ния/записи (cmReadWrite)

Connected Признак того, что соединение установлено

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


1. Сначала в окне Object Inspector надо выбрать свойство ConnectionString
и сделать щелчок на кнопке с тремя точками, которая находится в поле
значения этого свойства.
2. Затем в появившемся окне (рис. 6.5) надо нажать кнопку Build.
3. После этого в открывшемся окне Свойства связи с данными на вкладке
Поставщик данных нужно выбрать тип источника данных (для базы дан-
ных Microsoft Access — это Microsoft Jet 4.0 OLE DB Provider) и щелк-
нуть на кнопке Далее (рис. 6.6).
232 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 6.5. Настройка соединения с базой данных (шаг 1)

Рис. 6.6. Настройка соединения с базой данных (шаг 2)

4. Затем на вкладке Подключение (рис. 6.7) надо задать базу данных — сде-
лать щелчок на кнопке с тремя точками (...) и в открывшемся окне выбрать
файл базы данных. Если для доступа к базе необходим пароль и иденти-
фикатор пользователя, то их надо указать (по умолчанию к базе данных,
созданной в Microsoft Access, доступ есть у пользователя Admin, но па-
роль для доступа не нужен).
Ãëàâà 6. Áàçû äàííûõ 233

Рис. 6.7. Настройка соединения с базой данных (шаг 3)

5. После этого можно сделать щелчок на кнопке Проверить подключение,


убедиться, что соединение с базой данных настроено правильно, и щелч-
ком на кнопке OK закрыть окно Свойства связи с данными.
6. После этого, если для доступа к базе данных пароль не нужен, необходимо
присвоить значение false свойству LoginPrompt.
Значения свойств компонента ADOConnection1 после его настройки приведены
в табл. 6.3.

Òàáëèöà 6.3. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà ADOConnection1

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

Name ADOConnection1
ConnectionString Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=D:\Database\contacts.mdb;
Persist Security Info=false
LoginPrompt false
Connected false
234 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

После того как будет настроен компонент ADOConnection, можно приступить


к настройке компонента ADODataSet.
Компонент ADODataSet (набор данных) хранит данные, полученные из базы
данных. Свойства компонента ADODataSet приведены в табл. 6.4.

Òàáëèöà 6.4. Ñâîéñòâà êîìïîíåíòà ADODataSet

Свойство Описание

Connection Ссылка на компонент (ADOConnection), который обеспечивает


соединение с источником (базой) данных
CommandText Команда, которая направляется серверу
Parameters Параметры команды
Filter Фильтр. Позволяет отобрать записи, удовлетворяющие критерию
отбора
Filtered Признак использования фильтра
Activate Открывает или делает недоступным набор данных

В базе данных contacts.mdb информация хранится в таблице Contacts. Для


того чтобы информация из этой таблицы попала в компонент ADODataSet, в
свойство CommandText нужно записать SQL-команду, обеспечивающую выбор
необходимой информации. Выбор информации из таблицы базы данных
обеспечивает команда SELECT. В простейшем случае, когда надо получить всю
информацию, которая находится в таблице, в качестве параметров команды
SELECT нужно указать таблицу, имена полей и, возможно, поле, по содержи-
мому которого данные должны быть упорядочены. Например, SQL-команда,
обеспечивающая чтение данных из таблицы Contacts, выглядит так:
SELECT Name, Phone FROM Contacts ORDER BY Name

Значения свойств компонента ADODataSet1 приведены в табл. 6.5.

Òàáëèöà 6.5. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà ADODataSet1

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

Name ADODataSet1

Connection ADOConnection1

CommandText SELECT Name, Phone FROM Contacts ORDER BY Name

Activate false
Ãëàâà 6. Áàçû äàííûõ 235

Завершив настройку компонента ADODataSet, можно приступить к настройке


компонента DataSource — задать значение свойства DataSet, которое опреде-
ляет набор данных, связь с которым обеспечивает компонент (табл. 6.6).

Òàáëèöà 6.6. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà DataSource1

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

DataSet ADODataSet1

Îòîáðàæåíèå äàííûõ
Пользователь может работать с базой данных в режиме таблицы или в режи-
ме формы. В режиме таблицы информация отображается в виде таблицы, что
позволяет видеть одновременно несколько записей. Этот режим обычно ис-
пользуется для просмотра информации. В режиме формы отображается одна
запись. Обычно этот режим используется для ввода и редактирования ин-
формации. Часто эти два режима комбинируют, краткая информация (содер-
жимое ключевых полей) выводится в табличной форме, а при необходимости
видеть содержимое всех полей выполняется переключение в режим формы.
Отображение данных в табличной форме обеспечивает компонент DBGrid
(рис. 6.8). Свойства компонента (табл. 6.7) определяют вид таблицы и дей-
ствия, которые могут быть выполнены над данными во время работы про-
граммы.

DBGrid
Рис. 6.8. Значок компонента DBGrid

Òàáëèöà 6.7. Ñâîéñòâà êîìïîíåíòà DBGrid

Свойство Описание

DataSource Ссылка на источник данных (например, ADODataSet)

Columns Отображаемая информация (столбцы)


BorderStyle Вид границы вокруг компонента
236 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 6.7 (îêîí÷àíèå)

Свойство Описание
Options.dgEditing Разрешает (true) изменение, добавление и удаление
данных. Чтобы во время работы программы активизи-
ровать режим редактирования записи, надо нажать
клавишу <F2>; чтобы добавить запись — клавишу
<Insert>; чтобы удалить запись — комбинацию клавиш
<Ctrl>+<Del> или клавишу <Del> (если значение свой-
ства Options.dgConfirmDelete равно false)
Options.dgConfirmDelete Необходимость подтверждения удаления записи.
Если значение свойства равно true, то чтобы удалить
запись, пользователь должен нажать комбинацию кла-
виш <Ctrl>+<Del> и подтвердить выполнение операции
удаления щелчком на кнопке OK в появившемся окне
Confirm.
Если значение свойства равно false, то текущая
запись будет удалена в результате нажатия клавиши
<Del>
Options.dgTitles Разрешает вывод строки заголовка столбцов
Options.dgIndicator Разрешает (true) отображение колонки индикатора. Во
время работы с базой данных текущая запись помеча-
ется в колонке индикатора треугольником, новая
запись — звездочкой, редактируемая — специальным
значком
Options.dgColumnResize Разрешает (true) менять во время работы программы
ширину колонок таблицы
Options.dgColLines Разрешает (true) выводить линии, разделяющие ко-
лонки таблицы
Options.dgRowLines Разрешает (true) выводить линии, разделяющие стро-
ки таблицы

Свойство Colunns компонента DBGrid представляет собой коллекцию (мас-


сив) объектов типа TColumn. Свойства объекта TColumn (табл. 6.8) определяют
информацию, которая отображается в колонке.

Òàáëèöà 6.8. Ñâîéñòâà îáúåêòà TColumn

Свойство Описание
FieldName Поле, содержимое которого отображается в колонке
Width Ширина колонки в пикселах
Ãëàâà 6. Áàçû äàííûõ 237

Òàáëèöà 6.8 (îêîí÷àíèå)

Свойство Описание
Font Шрифт, используемый для отображения текста в ячейках
колонки
Color Цвет фона
Alignment Способ выравнивания текста в ячейках колонки. Текст мо-
жет быть выровнен по левому краю (taLeftJustify), по
центру (taCenter) или по правому краю (taRightJustify)
Title.Caption Заголовок колонки. По умолчанию в заголовке отображает-
ся имя поля
Title.Alignment Способ выравнивания заголовка. Заголовок может быть
выровнен по левому краю (taLeftJustify), по центру
(taCenter) или по правому краю (taRightJustify)
Title.Color Цвет фона заголовка колонки
Title.Font Шрифт заголовка колонки

Настройка компонента DBGrid выполняется следующим образом:


1. Сначала в коллекцию Columns надо добавить столько элементов, сколько
столбцов данных необходимо отобразить в поле компонента DBGrid. Для
этого следует раскрыть окно редактора коллекции — щелкнуть на кнопке
с тремя точками, которая находится в поле значения свойства Columns, или
из контекстного меню, появляющегося в результате щелчка правой кноп-
кой мыши в поле компонента, выбрать команду Columns Editor.
2. В окне редактора коллекции (рис. 6.9) надо сделать щелчок на кнопке Add
New. В результате в коллекцию Columns будет добавлен новый элемент —
объект TColumns.

Рис. 6.9. Чтобы добавить элемент в коллекцию Columns,


надо сделать щелчок на кнопке Add New
238 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

3. Добавив нужное количество элементов в коллекцию Columns, можно при-


ступить к их настройке. В простейшем случае для каждой колонки доста-
точно установить значение свойств FieldName и Title.Caption.
В табл. 6.9 приведены значения свойств компонента DBGrid1, а на рис. 6.10 —
вид формы после его настройки.

Òàáëèöà 6.9. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà DBGrid1

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

Font.Name Tahoma
Font.Size 9
Columns[0].FieldName Name
Columns[0].TitleCaption Имя
Columns[0].Title.Width 354
Columns[0].Title.Font.Style fsBold
Columns[1].FieldName Phone
Columns[1].TitleCaption Телефон
Columns[1].Width 116
Columns[1].Title.Font.Style fsBold

Рис. 6.10. Вид формы после настройки компонента DBGrid


Ãëàâà 6. Áàçû äàííûõ 239

Следующее, что надо сделать, — создать процедуры обработки событий


Activate и Close формы (листинг 6.1). Функция обработки события Activate
должна открыть базу данных, функция обработки события Close — сохра-
нить изменения, сделанные пользователем. Здесь нужно обратить внимание
на то, что изменения, сделанные пользователем, автоматически фиксируются
в базе данных в момент перехода к следующей записи. Однако если пользо-
ватель, не завершив ввод данных, закроет окно программы, то сделанные из-
менения не будут записаны в базу данных. Поэтому в момент завершения
работы программа должна проверить, не редактирует ли пользователь запись,
и если редактирует (в этом случае значение свойства EditorMode компонента
DBGrid равно true), то сохранить ее.

Листинг 6.1. База данных Контакты

#include <oleauto.hpp> // чтобы был доступен класс


// Oleauto::EOleException

// Начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
{
try
{
ADOConnection1->Open();
ADODataSet1->Active = true;
}
catch (Oleauto::EOleException &e) {
MessageDlg("Ошибка доступа к базе данных.\n" + e.Message,
mtError, TMsgDlgButtons()<<mbOK,0);
DBGrid1->Enabled = false;
}
}

// завершение работы программы


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if (DBGrid1->EditorMode) // пользователь не завершил редактирование
{
// сохранить редактируемую запись
ADODataSet1->UpdateBatch(arCurrent);
}
}
240 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Âûáîð èíôîðìàöèè èç áàçû äàííûõ


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

SQL-çàïðîñ
Чтобы выбрать из базы данных только нужные записи, надо направить серве-
ру SQL-команду SELECT, указав в качестве параметра критерий отбора запи-
сей.
В общем виде SQL-команда SELECT выглядит так:
SELECT СписокПолей FROM Таблица WHERE (Критерий) ORDER BY СписокПолей

Параметр Таблица задает таблицу базы данных, из которой надо выбрать дан-
ные. Параметр СписокПолей, указанный после слова SELECT, задает поля, со-
держимое которых надо получить (если необходимы данные из всех полей,
то вместо списка полей можно указать "звездочку"). Параметр Критерий зада-
ет критерий (условие) отбора записей. Параметр СписокПолей, указанный по-
сле ORDER BY, задает поля, по содержимому которых будут упорядочены ото-
бранные записи.
Например, команда
SELECT Name, Phone FROM Contacts WHERE Name = 'Культин Н.Б.'

обеспечивает выборку из таблицы Contacts записи, у которой в поле Name


находится текст "Культин Н.Б.".
В критерии запроса (при сравнении строк) вместо конкретного значения
можно указать шаблон. Например, шаблон Ку% обозначает все строки, кото-
рые начинаются с Ку, а шаблон %Ку% — все строки, в которых есть подстрока
Ку. При использовании шаблонов вместо оператора = надо использовать опе-
ратор LIKE.
Например, запрос
SELECT * FROM Contacts WHERE Name LIKE 'Ку%'

выберет из таблицы Contacts только те записи, в поле Name которых находит-


ся текст, начинающийся с Ку. Вместо оператора LIKE можно использовать
оператор CONTAINING (содержит). Например, приведенный ранее запрос,
Ãëàâà 6. Áàçû äàííûõ 241

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


ются с Ку, при использовании оператора CONTAINING будет выглядеть так:
SELECT * FROM Contacts WHERE Name CONTAINING 'Ку'

Следующая программа (ее главная форма приведена на рис. 6.11) демонстри-


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

Рис. 6.11. Форма программы работы с базой данных

Рассматриваемая программа является многооконным приложением. В глав-


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

Рис. 6.12. Окно Найти

Главная форма создается автоматически в момент начала работы над новым


проектом. Чтобы создать форму Найти, необходимо в меню File выбрать
команду NewForm – Delphi Win32. После того как форма Запрос будет
настроена (рис. 6.13, табл. 6.10 и 6.11), ее надо сохранить в каталоге проек-
та — выбрать в меню File команду Save. Здесь следует обратить внимание на
242 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

свойство ModalResult кнопки Button1. По умолчанию значение свойства рав-


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

Рис. 6.13. Форма Найти

Òàáëèöà 6.10. Çíà÷åíèÿ ñâîéñòâ ôîðìû Íàéòè

Свойство Значение
Name Form2
BorderStyle bsSingle
BorderIcons.Minimize false

BorderIcons.Maximize false
Position poMainFormCenter

Òàáëèöà. 6.11. Çíà÷åíèÿ ñâîéñòâ êíîïêè OK ôîðìû Íàéòè

Свойство Значение
Enabled false
ModalResult mrOk

Завершив настройку главной формы и формы Найти, можно приступить к


созданию процедур обработки событий. Процедуры обработки событий глав-
ной формы приведены в листинге 6.2, формы Найти — в листинге 6.3. Сле-
дует обратить внимание на то, что в модуль главной формы надо добавить
ссылку на модуль формы Найти — директиву #include "Unit2.h" (Unit2 —
имя модуля формы Найти).
Ãëàâà 6. Áàçû äàííûõ 243

Листинг 6.2. Модуль главной формы (Unit1.cpp)

// чтобы форма Найти (Form2) была доступна, в программу


// надо вставить ссылку на файл Unit2.h

#include "Unit2.h"

__fastcall TForm1::TForm1(TComponent* Owner)


: TForm(Owner)
{
}

// Начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
{
try
{
ADOConnection1->Open();
ADODataSet1->Active = true;
}
catch (Oleauto::EOleException &e) {
MessageDlg("Ошибка доступа к файлу базы данных.\n" + e.Message,
mtError, TMsgDlgButtons()<<mbOK,0);
}
}

// Щелчок на кнопке Найти


void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form2->ShowModal();
if ( Form2->ModalResult == mrOk)
{
// пользователь ввел критерий запроса
// и нажал кнопку OK
ADODataSet1->Close();
ADODataSet1->CommandText =
// 042 — код символа "двойная кавычка"
"SELECT * FROM Contacts WHERE Name Like \042%" +
Form2->Edit1->Text + "%\042";

// отладочная печать
ShowMessage(ADODataSet1->CommandText); // показать SQL-команду
}
244 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

ADODataSet1->Open();
if (ADODataSet1->RecordCount == 0)
{
// В базе данных нет записей,
// удовлетворяющих критерию запроса.
ADODataSet1->Filtered = false;
ShowMessage
("В БД нет записей, удовлетворяющих критерию запроса");
}
}

// Щелчок на кнопке Все записи


void __fastcall TForm1::Button2Click(TObject *Sender)
{
ADODataSet1->Close();
ADODataSet1->CommandText =
"SELECT * FROM Contacts ORDER BY Name";
ADODataSet1->Open();
}

// завершение работы программы


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if (DBGrid1->EditorMode == true) // пользователь не завершил
// редактирование
{
// сохранить редактируемую запись
ADODataSet1->UpdateBatch(arCurrent);
ADODataSet1->Close();
}
}

Листинг 6.3. Модуль формы Найти (Unit2.cpp)

/*
Это окно отображается как модальный диалог.
Чтобы в результате щелчка на кнопке OK окно закрылось,
и вновь стало доступным главное окно, свойству ModalResult
кнопки Button1 надо присвоить значение mrOK.
*/

__fastcall TForm2::TForm2(TComponent* Owner)


: TForm(Owner)
{
}
Ãëàâà 6. Áàçû äàííûõ 245

// окно появилось на экране


void __fastcall TForm2::FormActivate(TObject *Sender)
{
Edit1->Clear();
Edit1->SetFocus();
}

// изменилось содержимое поля редактирования


void __fastcall TForm2::Edit1Change(TObject *Sender)
{
if ( Edit1->Text.Length() > 0 )
Button1->Enabled = true;
else
Button1->Enabled = false;

// нажатие клавиши в поле редактирования


void __fastcall TForm2::Edit1KeyPress(TObject *Sender, char &Key)
{
if ( (Key == VK_RETURN) && ( Edit1->Text.Length() > 0))
Button1->SetFocus();
}

Ôèëüòð
Часто нужная пользователю информация уже есть в загруженной таблице.
В этом случае, для того чтобы ее выбрать (скрыть ненужную в данный
момент информацию), можно воспользоваться механизмом фильтрации
записей.
Фильтр — это условие отбора записей. Возможностью фильтрации обладают
компоненты ADODataset, ADOQuery и ADOTable. Для того чтобы фильтрация бы-
ла выполнена, в свойство Filter надо записать условие отбора записей и ак-
тивизировать процесс фильтрации — присвоить значение true свойству
Filtered (чтобы отменить действие фильтра, свойству Filtered надо присво-
ить значение false). Следует обратить внимание на то, что фильтр воздейст-
вует на набор данных, сформированный в результате выполнения команды
SELECT. Принципиальное отличие механизма фильтрации от выборки записей
командой SELECT состоит в том, что фильтр воздействует на записи, уже за-
груженные из базы данных (скрывает записи, не удовлетворяющие критерию
запроса), в то время как команда SELECT загружает из базы данных записи,
удовлетворяющие критерию запроса.
246 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

В качестве примера использования фильтра, в листинге 6.4 приведены про-


цедуры обработки событий Click для кнопок Найти и Все записи програм-
мы работы с базой данных Контакты.

Листинг 6.4. Обработка событий Click на кнопках Найти и Все записи

// Щелчок на кнопке Найти


void __fastcall TForm1::Button1Click(TObject *Sender)
{
Form2->ShowModal();
if ( Form2->ModalResult == mrOk)
{
// пользователь ввел критерий запроса
// и нажал кнопку OK

// фильтр
ADODataSet1->Filtered = false;
ADODataSet1->Filter ="name Like %"+
Form2->Edit1->Text + "%";
ADODataSet1->Filtered = true;
if ( ADODataSet1->RecordCount == 0 )
{
// В базе данных нет записей,
// удовлетворяющих критерию запроса.
ADODataSet1->Filtered = false;
ShowMessage
("В БД нет записей, удовлетворяющих критерию запроса");
}
}
}

// Щелчок на кнопке Все записи


void __fastcall TForm1::Button2Click(TObject *Sender)
{
ADODataSet1->Filtered = false;
}

Ðàáîòà ñ áàçîé äàííûõ â ðåæèìå ôîðìû


Существуют два режима отображения данных: таблица и форма.
В режиме таблицы в окне программы отображается таблица, что позволяет
видеть несколько записей одновременно. Обычно этот режим используется
Ãëàâà 6. Áàçû äàííûõ 247

для просмотра записей. Отображение данных в режиме таблицы обеспечива-


ет компонент DBGrid. Если в таблице, содержимое которой отображается в
поле компонента DBGrid, много колонок, то пользователь, как правило, не
может видеть все столбцы одновременно, и для того чтобы увидеть нужную
информацию, он вынужден менять ширину столбцов или прокручивать со-
держимое поля компонента по горизонтали, что не совсем удобно.
В режиме формы в окне программы отображается только одна запись, что
позволяет одновременно видеть содержимое всех полей записи. Обычно ре-
жим формы используется для ввода информации в базу данных, а также для
просмотра записей, состоящих из большого количества полей. Часто режим
формы и режим таблицы комбинируют.
Компоненты, обеспечивающие просмотр и редактирование полей, находятся
на вкладке Data Controls (рис. 6.14). На практике наиболее часто использу-
ются компоненты DBEdit и DBMemo. Они являются аналогами компонентов
Edit и Мемо и ориентированы на работу с базами данных. Свойства компо-
нентов DBEdit и DBMemo, обеспечивающие работу с базой данных, приведены в
табл. 6.12.

Рис. 6.14. Компоненты DBEdit и DBMemo обеспечивают редактирование полей записей


базы данных, компонет DBNavigator — навигацию по БД

Òàáëèöà 6.12. Ñâîéñòâà êîìïîíåíòîâ DBEdit è DBMemo

Свойство Описание
DataSource Источник данных
DataField Поле записи базы данных, содержимое которого отобража-
ется в поле компонента

В качестве примера рассмотрим программу (ее форма приведена на


рис. 6.15), которая обеспечивает работу с базой Контакты, но уже в режиме
формы.
Компоненты DBEdit и DBMemo обеспечивают отображение полей текущей
записи, компонент Image — отображение иллюстрации, имя файла которой
находится в поле image. Соединение с базой данных обеспечивает компо-
нент ADOConnection, а доступ к данным, находящимся в таблице Contacts, —
248 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

компонент ADODataSet. Значения свойств этих компонентов приведены в


табл. 6.13.

Рис. 6.15. Форма программы работы с базой данных Контакты (режим формы)

Òàáëèöà 6.13. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


ADOConnection1 ConnectionString Provider=Microsoft.Jet.OLEDB.4.0;
Data Source=D:\Database\contacts.mdb;
Persist Security Info=false
LoginPrompt false
ADODataSet1 Connection ADOConnection1
CommandText SELECT * FROM contacts
DataSource1 DataSet ADOTable1
DBEdit1 DataSource DataSource1
DataField Name
AutoSelect false
ReadOnly true
DBEdit2 DataSource DataSource1
DataField Phone
AutoSelect false
ReadOnly true
Ãëàâà 6. Áàçû äàííûõ 249

Òàáëèöà 6.13 (îêîí÷àíèå)

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


DBMemo DataSource DataSource1
DataField Comment
Image1 Proportional true
Enabled false
Hint Щелкните, чтобы изменить
ShowHint true

Компонент DBNavigator (его свойства приведены в табл. 6.14) обеспечивает


перемещение указателя текущей записи к следующей, предыдущей, первой
или последней записи, а также выполнение других операций, которые выпол-
няются в результате щелчка на соответствующей кнопке (табл. 6.15). Следует
обратить внимание на свойство VisibleButtons. Оно позволяет скрыть неко-
торые кнопки компонента DBNavigator и тем самым запретить выполнение
соответствующих операций над файлом данных. Например, присвоив значе-
ние false свойству VisibleButtons.nbDelete, можно скрыть кнопку nbDelete
и тем самым запретить удаление записей.
Значения свойств компонента DBNavigator1 приведены в табл. 6.16.

Òàáëèöà 6.14. Ñâîéñòâà êîìïîíåíòà DBNavigator

Свойство Определяет
DataSource Источник данных. В качестве источника данных может вы-
ступать, например, компонент ADODataSet, ADOTable или
ADOQuery
VisibleButtons Кнопки, которые отображаются в поле компонента. Скрыв
некоторые кнопки, можно запретить выполнение соответст-
вующих действий

Òàáëèöà 6.15. Êíîïêè êîìïîíåíòà DBNavigator

Кнопка Обозначение Действие

Указатель текущей записи перемеща-


К первой nbFirst
ется к первой записи файла данных
Указатель текущей записи
К предыдущей nbPrior перемещается к предыдущей записи
файла данных
250 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 6.15 (îêîí÷àíèå)

Кнопка Обозначение Действие

Указатель текущей записи перемеща-


К следующей nbNext ется к следующей записи файла дан-
ных
Указатель текущей записи перемеща-
К последней nbLast
ется к последней записи файла данных
В файл данных добавляется новая
Добавить nbInsert
запись
Удаляется текущая запись файла дан-
Удалить nbDelete
ных
Устанавливает режим редактирования
Редактирование nbEdit
текущей записи
Изменения, внесенные в текущую
Сохранить nbPost
запись, записываются в файл данных
Отменяет внесенные в текущую запись
Отменить Cancel
изменения
Записывает внесенные изменения
Обновить nbRefresh
в файл

Òàáëèöà 6.16. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà DBNavigator1

Свойство Значение
DataSource DataSource1
VisibleButtons.bnRefresh false
ShowHint true
Hints Первая запись
Предыдущая запись
Следующая запись
Последняя запись
Добавить запись
Удалить запись
Редактировать запись
Сохранить изменения
Отменить изменения

Текст программы приведен в листинге 6.5.


Ãëàâà 6. Áàçû äàííûõ 251

Функция обработки события AfterScroll (оно возникает после того, как в ре-
зультате нажатия кнопки компонента DBNavigator указатель текущей записи
будет перемещен к следующей или предыдущей записи) компонента
DBNavigator инициирует процесс отображения иллюстрации.
Непосредственное отображение иллюстрации обеспечивает функция
ShowImage, которой в качестве параметра передается содержимое поля Image
(или пустая строка, если поле пустое). Она выводит иллюстрацию или (если
поле Image текущей записи пустое) картинку nobody.jpg.
Функция обработки события Click компонента Image1 обеспечивает ввод
информации в поле Image (обратите внимание: в окне программы отобража-
ется иллюстрация, а не содержимое поля Image). Она открывает диалог Вы-
бор иллюстрации (компонент OpenDialog), в котором пользователь может
выбрать картинку. Если иллюстрация выбрана, то имя файла иллюстрации
записывается в поле Image текущей записи БД, а сам файл копируется в ката-
лог Images.

Листинг 6.5. Программа работы с базой данных Контакты (режим формы)

// Начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
{
try {
ADOConnection1->Open();
ADODataSet1->Open();
StatusBar1->Panels->Items[0]->Text = "Запись: 1";
}
catch (Exception &e)
{
DBEdit1->Enabled = false;
DBEdit2->Enabled = false;
DBNavigator1->Enabled = false;
MessageDlg("" +
aPath + "contacts.mdb",mtError,TMsgDlgButtons()<<mbOK,0);
}
}

// отображает иллюстрацию
void __fastcall TForm1::ShowImage(AnsiString img)
{
if ( img == "")
img = "nobody.jpg";
252 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

try
{
Image1->Picture->LoadFromFile(aPath+"images\\"+img);
}
catch (Exception &e){}
}

// переход к следующей записи


void __fastcall TForm1::ADODataSet1AfterScroll(TDataSet *DataSet)
{
AnsiString img;

if ( ADODataSet1->RecNo != -1 )
{
StatusBar1->Panels->Items[0]->Text =
" Запись: " + IntToStr(ADODataSet1->RecNo);
StatusBar1->Panels->Items[1]->Text = "";

// если поле image не содержит данных,


// то возникает исключение
try
{
img = ADODataSet1->FieldValues["image"];
}
catch (Exception &e)
{
img = "";
}

ShowImage(img);
}
else
StatusBar1->Panels->Items[0]->Text = " Новая запись" ;
}

// щелчок на кнопке компонента DBNavigator


void __fastcall TForm1::DBNavigator1Click(TObject *Sender, TNavigateBtn
Button)
{
switch ( Button )
{
case nbInsert:
StatusBar1->Panels->Items[0]->Text = "Новая запись";
Ãëàâà 6. Áàçû äàííûõ 253

DBEdit1->ReadOnly = false;
DBEdit2->ReadOnly = false;
DBMemo1->ReadOnly = false;
ShowImage("nobody.jpg");
Image1->Enabled = true;
break;

case nbEdit:
StatusBar1->Panels->Items[1]->Text = "Редактирование";
DBEdit1->ReadOnly = false;
DBEdit2->ReadOnly = false;
DBMemo1->ReadOnly = false;
Image1->Enabled = true;
break;

case nbFirst:
case nbPrior:
case nbNext:
case nbLast:
case nbPost:
case nbCancel:
DBEdit1->ReadOnly = true;
DBEdit2->ReadOnly = true;
DBMemo1->ReadOnly = true;
Image1->Enabled = false;
StatusBar1->Panels->Items[1]->Text ="";
break;

case nbDelete: break;


}
}

// щелчок на иллюстрации
void __fastcall TForm1::Image1Click(TObject *Sender)
{
AnsiString nFileName;

OpenDialog1->FileName = "*.jpg";
if ( OpenDialog1->Execute() )
{
// пользователь выбрал изображение
nFileName = ExtractFileName(OpenDialog1->FileName);

nFileName = aPath + "images\\" + nFileName;


254 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

// скопировать изображение в каталог Image


CopyFile( OpenDialog1->FileName.c_str(), nFileName.c_str(), false);

ShowImage(nFileName);
ADODataSet1->FieldValues["image"] = nFileName;
}
}

// завершение работы программы


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
if ( ADODataSet1->State == dsEdit)
// пользователь не завершил редактирование
// записать редактируемую запись в БД
ADODataSet1->UpdateBatch(arCurrent);
ADODataSet1->Close();
}

Çàãðóçêà ñòðîêè ñîåäèíåíèÿ èç ini-ôàéëà


Рассмотренные программы работы с базой данных Контакты для соединения с
источником данных используют компонент ADOConnection, свойство
ConnectionString которого жестко задает имя и путь к файлу базы данных.
Поэтому при установке программы работы с базой данных на другой компь-
ютер необходимо, чтобы каталог базы данных находился на том же диске,
что и на компьютере программиста, что не всегда удобно, а иногда и не вы-
полнимо. Избежать подобных проблем можно, если строку соединения за-
гружать из ini-файла в начале работы программы. Задачу загрузки или фор-
мирования строки соединения с учетом реального размещения базы данных
можно возложить на функцию обработки события BeforeConnect компонента
ADOConnection, которое происходит непосредственно перед подключением к
базе данных. Приведенная в листинге 6.6 функция показывает, как это можно
сделать. В данном примере из ini-файла (листинг 6.7) загружается не строка
соединения, а только имя каталога базы данных, после чего строка соедине-
ния формируется путем замены фрагмента.

Листинг 6.6. Обработка события BeforeConnection

void __fastcall TForm1::ADOConnection1BeforeConnect(TObject *Sender)


{
int p1,p2;
TIniFile* iniFile;
AnsiString fn; // имя ini-файла
AnsiString st; // строка соединения
Ãëàâà 6. Áàçû äàííûõ 255

// ini-файл находится в том же каталоге, что и exe-файл,


// его имя совпадает с именем exe-файла, но расширение — ini
fn = ReplaceStr(Application->ExeName,".exe",".ini");
iniFile = new TIniFile(fn);

// прочитаем из ini-файла имя каталога,


// в котором находится БД
// ключ aPath находится в секции data
aPath = iniFile->ReadString("data","aPath","");

if ( aPath == "" )
MessageDlg("Ошибка чтения ключа aPath из файла " +
iniFile->FileName ,mtError, TMsgDlgButtons()<<mbOK,0);
else {
st = ADOConnection1->ConnectionString;
p1 = Pos("Data Source",st)-1;
p2 = PosEx(";",st,p1)-1;

st = LeftStr(st,p1) + "Data Source="+ aPath+ "contacts.mdb" +


RightStr(st, st.Length()-p2);

ADOConnection1->ConnectionString = st;
}
}

Листинг 6.7. Ini-файл

[data]
aPath=D:\Database\

Ñåðâåð InterBase
Сервер Borland InterBase 2007 является хорошей основой для построения ин-
формационных систем различного масштаба и назначения. Отличительной
особенностью сервера является высокая скорость работы, надежность, не-
сложный процесс развертывания и администрирования. Сервер может рабо-
тать на платформах Microsoft Windows (Microsoft Windows Vista, Microsoft
Windows Server 2003, Microsoft Windows XP, Microsoft Windows 2000), Linux
и Solaris.
Borland предоставляет пользователям несколько версий сервера InterBase 2007:
 InterBase Server Edition 2007;
 InterBase Server Edition 2007 Dual Core;
256 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

 InterBase Desktop Edition 2007;


 InterBase Developer Edition 2007.
Первые из трех перечисленных вариантов являются полнофункциональными
серверами. Сервер Dual Core оптимизирован для работы на компьютере с
двухъядерным процессором. Вариант Desktop представляет собой локальный
сервер баз данных. Версия Developer Edition предназначена для разработчи-
ков программного обеспечения.

Óñòàíîâêà
Установить Developer Edition версию сервера InterBase на компьютер разра-
ботчика или на другой, доступный по сети компьютер, можно с диска RAD
Studio, выбрав соответствующую команду в окне активизации процесса уста-
новки компонентов RAD Studio, или путем запуска программы ib_install (она
находится в каталоге InterBase установочного диска).
Во время установки программист может выбрать устанавливаемые компо-
ненты (рис. 6.16). По умолчанию на компьютер разработчика устанавливает-
ся сервер InterBase, клиент (библиотека gds32.dll, обеспечивающая соедине-
ние с сервером), документация, утилиты IBConsole, InterBase Server Manager,
isql, gsec и др.

Рис. 6.16. Начало установки InterBase

Процесс установки завершается регистрацией (если в окне выбора устанав-


ливаемых компонентов был установлен флажок Register), которая по умол-
Ãëàâà 6. Áàçû äàííûõ 257

чанию выполняется в режиме on-line (компьютер, на который устанавливает-


ся сервер, должен иметь доступ к Интернету). После ввода в окне регистра-
ции серийного номера продукта и ключа с Web-сервера Borland на компью-
тер передается файл активации.

Установка Developer-версии не предполагает наличие у разработчика серийно-


го номера и ключа продукта. Поэтому в окне выбора компонентов надо сбро-
сить флажок Register.

Хотя, как было сказано ранее, версия Developer не предполагает наличие у


разработчика серийного номера и ключа продукта, но тем не менее и она тре-
бует активации. Файл активации для версии developer (trial) можно получить,
зарегистрировавшись в CodeGear Developer Network.
Для этого следует на сайте CodeGear (www.codegear.com) раскрыть страни-
цу Downloads и выбрать ссылку InterBase. Затем на странице InterBase Trial
and Free Versions необходимо выбрать продукт, для которого нужен актива-
ционный файл, и заполнить регистрационную форму. В результате на e-mail-
адрес, указанный во время регистрации, придет письмо с прикрепленным ре-
гистрационным файлом (reg*.txt). Этот файл нужно поместить в подкаталог
License того каталога, в который установлен InterBase (по умолчанию это
C:\Borland\InterBase).
Çàïóñê
Чтобы запустить InterBase, надо в меню Все программы Borland выбрать
команду InterBase Server Manager и в появившемся окне сделать щелчок на

Рис. 6.17. Чтобы запустить InterBase, надо сделать щелчок на кнопке Start
258 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

кнопке Start (рис. 6.17). Следует обратить внимание: если во время запуска
будет указан режим автоматического старта (выбран переключатель Startup
Mode Automatic), то при следующем включении компьютера сервер будет
запущен автоматически.

Óòèëèòà IBConsole
В комплект поставки InterBase входит утилита IBConsole, которая позволяет
решать задачи администрирования сервера. С ее помощью можно, например,
открыть доступ к существующей базе данных, создать новую базу данных,
зарегистрировать пользователя и определить его полномочия.
Чтобы запустить IBConsole (рис. 6.18), надо в меню Программы выбрать
команду InterBaseIBConsole.

Рис. 6.18. Утилита IBConsole используется для администрирования сервера InterBase

Ðåãèñòðàöèÿ ñåðâåðà
Для того чтобы подключиться к серверу, надо в меню Server выбрать коман-
ду Add и в поля появившегося окна Add Server and Connect ввести инфор-
мацию о сервере.
Если InterBase работает на другом, доступном по сети, компьютере, то надо
выбрать Remote Server (удаленный), в поле Server Name ввести сетевое имя
Ãëàâà 6. Áàçû äàííûõ 259

компьютера, на котором установлен InterBase, а в поле Alias Name — имя,


которое предполагается использовать для доступа к серверу InterBase
(рис. 6.19).

Рис. 6.19. Настройка соединения с удаленным сервером

Следует обратить внимание: имя и IP-адрес компьютера, на котором работает


InterBase, должны быть прописаны в файле C:\Windows\System32\drivers
\etc\hosts компьютера-клиента (листинг 6.8). Узнать IP-адрес сервера можно,
запустив на нем системную утилиту ipconfig.exe.

Листинг 6.8. Файл hosts (фрагмент)

127.0.0.1 localhost
192.168.1.5 nk

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


то в поле Server Name следует ввести localhost (обратите внимание, имя
localhost есть в файле hosts).
После того как будет введена информация о сервере, надо установить флажок
Register Only и сделать щелчок на ставшей доступной кнопке OK. В резуль-
тате в списке InterBase Servers окна IBConsole появится имя сервера.
260 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Ïîäêëþ÷åíèå ê ñåðâåðó
Для того чтобы получить доступ (подключиться) к серверу, надо сделать
двойной щелчок на значке сервера и в появившемся окне (рис. 6.20) ввести
имя (User Name) и пароль (Password) пользователя, запрашивающего под-
ключение.

Рис. 6.20. После установки InterBase доступ к серверу имеет


только администратор — пользователь SYSDBA (пароль — masterkey)

Следует обратить внимание на то, что сразу после установки InterBase доступ
к серверу имеет только администратор — пользователь SYSDBA (пароль —
masterkey). Для того чтобы доступ к серверу был и у других пользователей,
администратор должен их зарегистрировать, затем определить полномочия
(указать базы данных, с которыми пользователь может работать, и операции,
которые он может выполнять над данными).
При подключении к удаленному серверу необходимо учитывать, что доступ к
серверу может быть ограничен брандмауэром. Для того чтобы клиент мог

Рис. 6.21. Чтобы клиент мог подключиться к серверу InterBase с другого компьютера,
надо открыть порт 3050
Ãëàâà 6. Áàçû äàííûõ 261

подключиться к серверу InterBase, на сервере должен быть открыть порт


3050. Чтобы открыть порт, надо через Центр обеспечения безопасности сна-
чала открыть брандмауэр, на вкладке Исключения сделать щелчок на кнопке
Добавить порт и в появившемся окне задать имя и номер порта (рис. 6.21).

Ðåãèñòðàöèÿ ïîëüçîâàòåëÿ
Как было сказано ранее, после установки InterBase доступ к серверу есть
только у администратора (пользователя SYSDBA). Чтобы и у других пользо-
вателей был доступ к серверу, администратор должен их зарегистрировать.
Чтобы зарегистрировать пользователя, администратор должен подключиться
к серверу, в контекстном меню элемента Users выбрать команду Add User
(рис. 6.22) и в поля появившегося окна User Information (рис. 6.23) ввести
информацию о новом пользователе: имя и пароль.

Рис. 6.22. Регистрация пользователя (шаг 1)

Ðåãèñòðàöèÿ ñóùåñòâóþùåé áàçû äàííûõ


Если файл базы данных существует, но имя базы данных в списке Databases
не отображается, то для того чтобы получить доступ к базе данных, надо в
меню Database выбрать команду Add, в появившемся окне (рис. 6.24) указать
файл базы данных и задать псевдоним. Если в этом же окне указать инфор-
мацию, необходимую для подключения к базе данных (User Name и
Password), то база данных будет не только зарегистрирована, но и открыта.
262 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 6.23. Регистрация пользователя (шаг2)

Рис. 6.24. Чтобы база данных стала доступна, ее надо зарегистрировать

Ñîåäèíåíèå ñ ÁÄ
Если база данных зарегистрирована (ее имя отображается в списке баз дан-
ных сервера), то для того чтобы получить к ней доступ (открыть соединение с
базой данных), надо выбрать базу данных (сделать щелчок на значке, ее изо-
Ãëàâà 6. Áàçû äàííûõ 263

бражающем), в меню Database выбрать команду Connect, а затем в появив-


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

Ñîçäàíèå áàçû äàííûõ


База данных (БД) — это совокупность таблиц, индексов, запросов и храни-
мых процедур. В InterBase все элементы, образующие базу данных, хранятся
в одном файле.
Чтобы создать базу данных, надо в меню Database выбрать команду Create
Database и в появившемся окне Create Database (рис. 6.25) задать имя файла
базы данных (c расширением ib) и псевдоним.
Имя файла базы данных можно задать, указав каталог, в который надо помес-
тить файл базы данных. Если каталог не указан, то этот файл будет создан в
каталоге InterBase (если база создается на удаленном сервере) или в каталоге
C:\Documents and Settings\User, где User — имя пользователя (если сервер
зарегистрирован как локальный).

Рис. 6.25. Чтобы создать базу данных, надо указать файл и псевдоним базы данных
264 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Псевдоним (имя, которое используется для доступа к базе данных) надо вве-
сти в поле Alias. Использование псевдонима вместо имени файла позволяет
серверу скрыть от пользователя имя файла базы данных, а также его физиче-
ское размещение на диске компьютера. Следует обратить внимание на фла-
жок Register database. Если он установлен (а по умолчанию это так), то соз-
данная в результате щелчка на кнопке OK база данных будет зарегистриро-
вана и станет доступной через IBConsole.
Пользователь, создавший базу данных, является ее владельцем. Он по отно-
шению к этой базе данных, обладая неограниченными полномочиями, может
создавать таблицы, наполнять их информацией, открывать базу данных для
других пользователей и определять их полномочия. Следует обратить внима-
ние на то, что доступ к только что созданной базе данных есть только у хо-
зяина (пользователя, который ее создал) и у администратора.

Ñîçäàíèå òàáëèöû
Чтобы создать в базе данных таблицу, надо направить серверу SQL-команду
CREATE TABLE.
Например, команда
CREATE TABLE Books (Title CHAR(60) NOT NULL, Author CHAR(30))
обеспечивает создание таблицы Books в текущей базе данных.

Рис. 6.26. Чтобы создать таблицу, надо набрать SQL-запрос


и активизировать процесс его выполнения (команда QueryExecute)
Ãëàâà 6. Áàçû äàííûõ 265

SQL-команды надо набирать в верхней части окна Interactive SQL


(рис. 6.26), которое становится доступным в результате выбора в меню Tools
команды Interactive SQL. После того как команда будет набрана, следует
активизировать процесс ее выполнения — выбрать в меню Query команду
Execute.

Ââîä äàííûõ â òàáëèöó


Чтобы ввести информацию в таблицу, надо направить серверу команду
INSERT INTO, указав в качестве параметров таблицу и данные.
Например, команда
INSERT INTO Books (Title, Author)
VALUES ('C++ Builder в примерах и задачах','Культин Н.Б.')

добавляет информацию (строку) в таблицу Books.


Данные в таблицу можно ввести также в режиме редактирования таблицы
(рис. 6.27). Для перехода в этот режим надо выбрать базу данных, раскрыть
список таблиц, сделать двойной щелчок на значке нужной таблицы и в по-
явившемся окне открыть вкладку Data.

Рис. 6.27. Ввод данных осуществляется на вкладке Data

Данные в таблицу вводят обычным образом. Для перехода к следующему по-


лю (столбцу таблицы) нужно нажать клавишу <Tab>. Если текущее поле яв-
266 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

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


клавиши <Tab> в таблицу будет добавлена строка (запись).
Если во время заполнения таблицы необходимо внести изменения в уже за-
полненное поле, то надо, используя клавиши перемещения курсора, выбрать
это поле и нажать клавишу <F2>.
В нижней части окна находятся кнопки, обеспечивающие навигацию по базе
данных, и кнопки управления редактированием (табл. 6.17).

Òàáëèöà 6.17. Êíîïêè íàâèãàöèè è óïðàâëåíèÿ ðåäàêòèðîâàíèåì ÁÄ

Кнопка Действие
Указатель текущей записи перемещается к первой
К первой
записи файла данных
Указатель текущей записи перемещается
К предыдущей
к предыдущей записи файла данных
Указатель текущей записи перемещается к следующей
К следующей
записи файла данных
Указатель текущей записи перемещается к последней
К последней
записи файла данных
Добавляет в таблицу перед текущей строкой пустую
Добавить
строку

Удалить Удаляет текущую строку таблицы

Редактирование Активизирует режим редактирования текущей строки

Изменения, внесенные в текущую строку, записывают-


Сохранить
ся в базу (файл) данных
Отменяет внесенные в текущую строку (запись)
Отменить
изменения
Записывает в файл изменения, внесенные в таблицу,
Обновить
или считывает данные из файла и обновляет таблицу

Óäàëåíèå òàáëèöû
Иногда возникает необходимость удалить таблицу из базы данных. Чтобы это
сделать, надо раскрыть список таблиц, сделать щелчок на значке таблицы,
которую надо удалить, и в контекстном меню выбрать команду Drop.

Óïðàâëåíèå äîñòóïîì ê áàçå äàííûõ


Иногда возникает необходимость закрыть базу данных, сделать ее недоступ-
ной из IBConsole. Чтобы это сделать, надо в меню Database выбрать команду
Ãëàâà 6. Áàçû äàííûõ 267

Remove. Если соединение с базой данных открыто (в этом случае команда


Remove будет недоступна), то его надо предварительно закрыть — выбрать в
меню Database команду Disconnect. В результате описанных действий база
данных станет недоступной, а ее имя будет удалено из списка доступных (за-
регистрированных) баз данных. Здесь следует обратить внимание, что коман-
да Remove не уничтожает файл базы данных, а только делает базу данных
недоступной.

Óòèëèòû gsec è isql


В комплект поставки InterBase входят работающие в режиме командной стро-
ки утилиты: gsec и isql. Утилита gsec позволяет выполнить настройку сер-
вера, isql — направить серверу SQL-команду и увидеть результат ее выпол-
нения. Обе утилиты (gsec.exe и isql.exe) находятся в каталоге InterBase\Bin.
Чтобы запустить утилиту gcec, сначала надо открыть окно командной строки
(ПускВсе программыСтандартныеКомандная строка) и сделать
текущим каталог C:\Borland\InterBase\Bin (набрать в окне командной строки
C:\Borland\InterBase\Bin и нажать клавишу <Enter>). Затем надо набрать
команду, обеспечивающую запуск утилиты gseс (рис. 6.28), указав в качестве
параметров свое имя (ключ -user) и пароль (ключ -password). Сделать это мо-
жет администратор или другой пользователь, имеющий доступ к серверу и
обладающий соответствующими полномочиями. Следует обратить внимание
на то, что если InterBase работает на удаленном компьютере, то в инструкции
запуска надо указать ключ –database и полное (т. е. с указанием имени серве-
ра и службы) имя файла системной базы данных (admin.ib).

Рис. 6.28. Запуск утилиты gsec


268 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Утилита gsec работает в режиме командной строки. Набранная команда, если


она правильная, выполняется в результате нажатия клавиши <Enter>.
Некоторые команды утилиты gsec приведены в табл. 6.18.

Òàáëèöà 6.18. Êîìàíäû óòèëèòû gsec

Команда Действие
display Выводит список пользователей, зарегистрированных
на сервере
add имя -pw пароль Регистрирует пользователя
modify имя –pw пароль Задает новый пароль для указанного пользователя
delete имя Закрывает пользователю, имя которого указано в ка-
честве параметра команды, доступ к серверу
alias_add псевдоним Регистрирует псевдоним. После того как псевдоним
–alias_dbpath файл_БД будет зарегистрирован, вместо имени файла базы
данных можно использовать псевдоним
alias_dis Выводит список зарегистрированных псевдонимов
help Выводит краткую справку (список команд)
quit Завершение работы с утилитой

Утилита isql позволяет соединиться с сервером и направить ему SQL-


команду. Чтобы запустить утилиту, надо открыть окно командной строки,
сделать текущим каталог C:\Borland\InterBase\Bin, набрать isql и нажать кла-
вишу <Enter>.
Соединение с базой данных обеспечивает команда CONNECT, которая в общем
виде выглядит так:
CONNECT БазаДанных USER Пользователь PASSWORD Пароль

Параметр БазаДанных, в качестве которого можно указать имя файла или


псевдоним базы данных, определяет базу данных, с которой надо установить
соединение. В общем случае перед именем файла или псевдонимом базы
данных надо указать сервер (сетевое имя компьютера, на котором работает
InterBase) и сервис (gds_db).
Параметры Пользователь и Пароль идентифицируют пользователя, запраши-
вающего подключение (предполагается, что у пользователя есть право досту-
па к серверу).
Например, если у пользователя NIKITA есть доступ к серверу nk, то для того
чтобы соединиться с базой данных books (здесь books — псевдоним), он дол-
жен набрать команду:
CONNECT nk/gds_db:books USER NIKITA PASSWORD nikita;
Ãëàâà 6. Áàçû äàííûõ 269

Чтобы SQL-команда, набранная в окне утилиты isql, была выполнена (отправ-


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

После того как соединение c базой данных будет установлено, пользователь


может направить серверу SQL-команду. Например, следующая команда
обеспечивает отображение таблицы books
SELECT * FROM BOOKS;

Чтобы завершить работу с утилитой isql, надо набрать команду quit.

Çàùèòà äàííûõ
Защита от несанкционированного доступа к данным с целью их просмотра
или изменения является важной проблемой. В InterBase реализована двух-
уровневая система контроля прав доступа к данным. На первом уровне кон-
тролируется право доступа к серверу, в момент подключения пользователь
должен указать Login и пароль. Подключение к серверу не означает получе-
ния доступа к данным, находящимся на сервере. Для того чтобы пользова-
тель, подключившийся к серверу, мог получить доступ к базе данных, владе-
лец базы данных должен открыть пользователю доступ, указав, какие опера-
ции и над какими объектами базы данных он может выполнять.

Àäìèíèñòðàòîð
Администратор (пользователь SYSDBA) обладает особыми полномочиями.
Только он может регистрировать новых пользователей и изменять информа-
цию о существующих.
После установки InterBase пароль администратора — masterkey. Так как это
известно всем, то сразу после установки сервера рекомендуется изменить па-
роль администратора. Сделать это можно при помощи утилиты gsec, напра-
вив серверу команду
modify sysdba –pw пароль
где пароль — новый пароль администратора.

Ïñåâäîíèì áàçû äàííûõ


Чтобы получить доступ к базе данных, клиент (программа работы с базой
данных) должен знать, где (на каком диске и в каком каталоге) на сервере
находится файл базы данных. Вместе с тем, предоставлять эту информацию
пользователю крайне нежелательно. Разрешить эту проблему можно, создав
на сервере псевдоним базы данных. После того как псевдоним будет создан,
клиент может его использовать вместо имени файла базы данных.
270 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Создать псевдоним может администратор, набрав в окне утилиты gsec


команду alais_add. Например, команда создания псевдонима books для базы
данных Книги (books.ib) выглядит так:
alias_add books -alias_bdpath D:\Database\books.ib

Ïîëíîìî÷èÿ ïîëüçîâàòåëÿ
По умолчанию доступ к базе данных есть только у ее владельца (пользовате-
ля, который ее создал) и администратора (SYSDBA). Для того чтобы другие
пользователи могли работать с базой данных, владелец базы должен ее для
них открыть. Сервер InterBase предоставляет широкие возможности по
управлению доступом к базе данных. В частности, разным пользователям
можно предоставить разные права (привилегии) доступа. Например, одному
пользователю можно разрешить только просматривать таблицы, причем не
все, а только некоторые, другому — вносить изменения (всего есть четыре
операции: просмотр, добавление, удаление и изменение).
Чтобы определить полномочия пользователя, надо направить серверу коман-
ду GRANT, указав в качестве параметров имя пользователя, объект и список
операций, которые пользователь может выполнять над указанными объек-
тами.
Например, команда
GRANT SELECT ON BOOKS TO guest
открывает пользователю guest таблицу BOOKS, но только для просмотра.
Если надо открыть доступ к базе данных нескольким пользователям, то вме-
сто того чтобы определять полномочия каждого из них, можно определить
роль, а затем назначить этим пользователям соответствующую роль.
Сначала надо создать роль — направить серверу команду CREATE ROLE. После
того как роль будет создана, нужно определить полномочия роли. Назначение
роли выполняет команда GRANT.
Например, последовательность команд
CREATE ROLE operator
GRANT SELECT, APPEND, UPDATE ON Books TO operator
GRANT Platon, Danila TO operator

создает роль operator, определяет ее полномочия (просмотр, добавление и


редактирование записей таблицы BOOKS) и добавляет в созданную группу
пользователей Danila и Platon.
Следует обратить внимание на то, что в каждой базе данных определена роль
PUBLIC (но полномочия ее по умолчанию не определены). Эта роль использу-
ется для определения полномочий обычных пользователей. Например, чтобы
Ãëàâà 6. Áàçû äàííûõ 271

разрешить пользователю guest (это имя обычно используется для анонимно-


го, не требующего аутентификации пользователя, подключения к серверу)
просматривать таблицу Books, достаточно направить серверу следующие
команды:
GRANT SELECT ON Books TO PUBLIC
GRANT guest TO PUBLUC

Ñöåíàðèè
Последовательность SQL-команд, обеспечивающая выполнение некоторой
задачи, например создание таблицы и наполнение ее информацией, называет-
ся сценарием.
Сценарий можно записать в файл, а затем активизировать процесс его вы-
полнения в автоматическом режиме.
В качестве примера в листинге 6.9 приведен сценарий, обеспечивающий соз-
дание базы данных Книги (books.ib).

Листинг 6.9. Сценарий создания базы данных Книги (books_crdb.sql)

CREATE DATABASE 'D:\Database\books.ib'


USER 'sysdba' PASSWORD 'masterkey';
CONNECT 'D:\Database\books.ib' USER 'sysdba' PASSWORD 'masterkey';
CREATE TABLE Books (Title VARCHAR(60) NOT NULL, Author VARCHAR(30));
INSERT INTO Books (Title,Author)
VALUES ('Delphi в примерах и задачах', 'Культин Н.Б.');
INSERT INTO Books (Title,Author)
VALUES ('C++ Builder в примерах и задачах', 'Культин Н.Б.');
GRANT SELECT ON Books TO PUBLIC;
COMMIT;

Чтобы сценарий был выполнен, в окне командной строки надо набрать


команду запуска утилиты isql, указав в качестве значения параметра input файл
сценария. Например, команда запуска сценария books_crdb.sql выглядит так:
isql.exe –input boos_crdb.sql

Ïðèëîæåíèå ðàáîòû ñ áàçîé äàííûõ InterBase


Процесс создания приложения работы с базой данных InterBase рассмотрим
на примере. Создадим программу, обеспечивающую работу с базой данных
Книги.
272 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Перед тем как приступить к непосредственной работе в C++ Builder, надо


создать базу данных Книги (books.ib), поместить в нее таблицу Books
(табл. 6.19), наполнить таблицу информацией и определить полномочия роли
PUBLIC (чтение данных из таблицы Books). Также надо на сервере создать
псевдоним books (указать, где находится файл базы данных) и зарегистриро-
вать пользователя guest (пароль guest).

Òàáëèöà 6.19. Ïîëÿ òaáëèöû Books

Поле Тип
Title VARCHAR(60)
Author VARCHAR(30)

Компоненты, обеспечивающие работу с базами данных InterBase, находятся


на вкладках InterBase, InterBase Admin и dbExpress.
На вкладке InterBase (рис. 6.29) сгруппированы компоненты, позволяющие
соединиться с базой данных (IBDatabase), прочитать из нее информацию
(IBDataSet), направить серверу SQL-запрос (IBQuery)

Рис. 6.29. Компоненты InterBase

Компоненты вкладки InterBase Admin обеспечивают решение задач админи-


стрирования сервера InterBase.
На вкладке IBExpress находятся компоненты, использующие для доступа к
данным разработанную Borland технологию dbExpress. Следует обратить
внимание на то, что компоненты dbExpress обеспечивают только однона-
правленный (unidirectional) доступ к базе данных. Это значит, что курсор те-
кущей записи можно переместить только к следующей записи. Поэтому для
просмотра базы данных, доступ к которой обеспечивают компоненты
dbExpress, нельзя напрямую использовать компонент DBGrid (при попытке
переместить указатель к предыдущей записи возникает ошибка).
Форма программы работы с базой данных Книги приведена на рис. 6.30, зна-
чения свойств компонентов — в табл. 6.20. Необходимо обратить внимание,
что настраивать компоненты и устанавливать значения свойств следует в том
порядке, в котором они перечислены в тaблице.
Ãëàâà 6. Áàçû äàííûõ 273

Рис. 6.30. Форма программы работы с базой данных Книги

Òàáëèöà 6.20. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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

IBDatabase1 DatabaseName localhost/gds_db:books

LoginPrompt true

Params user_name=guest

IBDataSet1 Database IBDatabase1

Transaction IBTransaction1
SelectSQL SELECT * FROM Books WHERE
UPPER(Title) CONTAINING UPPER(
:Title)
ORDER BY Author
InsertSQL INSERT INTO Books
(Author, Title)
values
(:Author, :Title)
DeleteSQL DELETE FROM Books
WHERE
Author = :OLD_Author AND
Title = :OLD_Title
274 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 6.20 (îêîí÷àíèå)

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


ModifySQL UPDATE Books
SET
Author = :Author,
TITLE = :Title
WHERE
Author = :OLD_Author AND
Title = :OLD_Title
RefreshSQL SELECT
Author,
Title
FROM Books
WHERE
Author = :Author AND
Title = :Title
IBTransaction1 DefaultDatabase IBDatabase1
DataSource1 DataSet IBDataSet1
DBGrid1 DataSource DataSet1
Font.Name Tahoma
Font.Size 9
Columns[0].FieldName Author
Columns[0].Title.Caption Автор
Columns[1].FieldName Title
Columns[1].Title.Caption Название
DBNavigator1 DataSource DataSource1

Обратите внимание на то, как записаны SQL-команды. Двоеточие, указанное


идентификатором, показывает, что идентификатор — это параметр, а не имя
поля базы данных. Значение параметра можно установить в коде, т. е. ис-
пользование параметров в SQL-командах (такие команды называются пара-
метризированными) позволяют формировать запросы во время работы про-
граммы.
Модуль программы работы с базой данных Книги приведен в листинге. 6.10.
Ãëàâà 6. Áàçû äàííûõ 275

Листинг 6.10. Модуль программы работы с БД Книги

// начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
{
Form1->IBDatabase1->Open();
IBDataSet1->Open(); // выполнить команду SELECT

// отобразить в строке состояния имя базы данных


// и имя пользователя
int p;
p = Pos("=",IBDatabase1->Params[0].Text);

StatusBar1->Panels->Items[0]->Text = IBDatabase1->DatabaseName;
StatusBar1->Panels->Items[1]->Text =
"USER: " + IBDatabase1->Params[0].Text.SubString(p+1,20);
}

// щелчок на кнопке Все записи


void __fastcall TForm1::Button1Click(TObject *Sender)
{
if (IBDataSet1->Active) {
IBDataSet1->Close();
}
IBDataSet1->ParamByName("Title")->AsString = "";
IBDataSet1->Open();
}

// Щелчок на кнопке Найти


void __fastcall TForm1::Button2Click(TObject *Sender)
{
IBDataSet1->Close();
IBDataSet1->ParamByName("Title")->AsString = Edit1->Text;
IBDataSet1->Open();
}

Окно программы работы с базой данных Книги приведено на рис. 6.31. Обра-
тите внимание: доступность отдельных кнопок компонента DBNavigator и,
следовательно, действия, которые может выполнить пользователь, определя-
ются его полномочиями (ролью). Так администратор (SYSDBA) может вы-
полнять любые действия (добавлять, удалять и редактировать записи), а guest
(роль PUBLIC) — только просматривать (см. лиcтинг 6.5).
276 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 6.31. Окно программы работы с базой данных Книги

В программе работы с базой данных Книги размещение файла данных опреде-


ляет свойство DatabaseName компонента IBDatabase, значение которого зада-
ется во время разработки программы. Поэтому, чтобы программа работала у
пользователя, имя компьютера, на котором установлен InterBase, должно
совпадать с именем, заданным программистом. На практике такое жесткое
требование выполнить, как правило, нельзя. Предотвратить возникновение
этой проблемы можно, если предоставить пользователю возможность указать
имя сервера и другие параметры соединения в файле настройки.
Для загрузки параметров соединения с базой данных InterBase из ini-файла
удобно использовать компонент IBDatabaseINI (его свойства приведены в
табл. 6.21).

Òàáëèöà 6.21. Câîéñòâà êîìïîíåíòà IBDatabaseINI

Свойство Описание

FileName Имя ini-файла


UseAppPath Задает размещение ini-файла
Section Имя секции в ini-файле, в которой находятся параметры, значе-
ния которых будут загружены (метод ReadFromINI) или записаны
(метод SaveToINI) в ini-файл
DatabaseName База данных — значение параметра DatabaseName
User Имя пользователя — значение параметра user_name
Password Пароль — значение параметра password
SQLRole Роль — значение параметра sql_role
Ãëàâà 6. Áàçû äàííûõ 277

Рис. 6.32. Компонент IBDatabaseINI обеспечивает загрузку параметров соединения

После того как компонент IBDatabaseINI будет добавлен на форму


(рис. 6.32), надо выполнить его настройку (табл. 6.22). Также надо очистить
значения свойств DatabaseName и Params компонента IBDatabase.

Òàáëèöà 6.22. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòà IBDatabaseINI

Свойство Значения
FileName books.ini
UseAppPath ipoPathNone
Section Database Settings

Загрузку параметров соединения из ini-файла (листинг 6.11) обеспечивает


функция обработки события FormActivate (листинг 6.12). Сначала метод
LoadFromINI загружает значения параметров в свойства компонента
IBDatabaseINI, затем они передаются в компонент IBDatabase. Создать ini-
файл можно вручную или путем вызова метода SaveToINI.

Листинг 6.11. books.ini

[Database Settings]
database=nk/gds_db:books
user_name=guest
password=
sql_role=
lc_ctype=
278 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Листинг 6.12. Функция обработки события FormActivate

// начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
{
// загрузить имя базы данных из ini-файла
// ini-файл находится в том же каталоге, что и exe-файл

AnsiString aPath; // каталог, в котором находится exe-файл

aPath = ExtractFilePath(Application->ExeName);
IBDatabaseINI1->FileName = aPath +"books.ini";

if (! FileExists(IBDatabaseINI1->FileName) )
{
// ini-файл не найден
IBDatabaseINI1->SaveToINI(); // создать ini-файл
}

IBDatabaseINI1->ReadFromINI(); // загрузить параметры из ini-файла

IBDatabase1->DatabaseName = IBDatabaseINI1->DatabaseName;
IBDatabase1->Params->Add("user_name="+IBDatabaseINI1->Username);

try {
IBDatabase1->Open();
} catch (EIBClientError &e) {
MessageDlg( e.Message + "\nСмотри: " + IBDatabaseINI1->FileName,
mtError, TMsgDlgButtons() << mbOK, 0);
return;
}

IBDataSet1->Open(); // выполнить команду SELECT

// отобразить в строке состояния имя базы данных


// и имя пользователя
int p;
p = Pos("=",IBDatabase1->Params[0].Text);

StatusBar1->Panels->Items[0]->Text = IBDatabase1->DatabaseName;
StatusBar1->Panels->Items[1]->Text =
"USER: " + IBDatabase1->Params[0].Text.SubString(p+1,20);
}
Ãëàâà 6. Áàçû äàííûõ 279

Êîìïîíåíòû dbExpress
Существует большое количество задач, в которых нет необходимости пре-
доставлять клиенту возможность интерактивного взаимодействия с базой
данных. Например, в справочно-информационных системах пользователь,
как правило, просматривает записи по одной (от первой до последней), пока
не найдет нужную информацию. Для построения таких систем можно ис-
пользовать компоненты dbExpress.
Компоненты dbExpress (рис. 6.33) обеспечивают однонаправленный (uni-
directional) доступ к базе данных. Это значит, что указатель текущей записи
можно перемещать только в одном направлении — вперед к следующей
записи (при попытке переместить указатель к предыдущей записи возникает
исключение).

Рис. 6.33. Компоненты dbExpress

Следующая программа (ее форма приведена на рис. 6.34) демонстрирует ис-


пользование компонентов dbExpress для доступа к базе данных InterBase.

Рис. 6.34. Форма программы просмотра базы данных Книги

Компоненты SQLConnection и SQLDataSet обеспечивают доступ к базе дан-


ных, компоненты DBEdit — отображение информации, компонент DataSource
связывает компонент SQLDataSet с компонентами отображения данных
(DBEdit) и навигации (DBNavigator).
Перед тем как приступить к настройке компонента SQLConnection, необходи-
мо создать соединение с базой данных: в окне Data Explorer сделать щелчок
280 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

правой кнопкой мыши в строке INTERBASE, в появившемся меню выбрать


команду Add New Connection (рис. 6.35, а), затем в окне Add New
Connection надо ввести имя соединения (рис. 6.35, б). После этого надо рас-
крыть список INTERBASE, сделать щелчок правой кнопкой мыши на имени
созданного соединения, выбрать команду Modify Connection и в появившем-
ся окне (рис. 6.36) ввести имя базы данных, доступ к которой должно обеспе-
чить настраиваемое соединение, а также имя и пароль пользователя.

а б
Рис. 6.35. Создание соединения с базой данных InterBase

Рис. 6.36. Настройка соединения


Ãëàâà 6. Áàçû äàííûõ 281

Информация о соединениях, созданных программистом, хранится в файле


C:\Documents and Settings\All Users\Документы\RAD Studio\dbExpress
\dbxconnections.ini.

После того как соединение с базой данных будет создано и настроено, можно
приступить к настройке компонентов (табл. 6.23). Значения свойств компо-
нентов необходимо устанавливать в том порядке, в котором они перечислены
в таблице. Следует обратить внимание: в процессе настройки компонента
SQLConnection параметры выбранного соединения копируются из файла
dbxconnections.ini в соответствующие поля свойства Params. Если свойству
LoadParamsOnConnect присвоить значение true, то параметры соединения бу-
дут загружаться из файла dbxconnections.ini при каждом запуске программы.

Òàáëèöà 6.23. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


SQLConnection1 Name SQLConnection1
ConnectionName books_ib
SQLDataSet1 Name SQLDataSet1
CommandText SELECT * FROM BOOKS
SQLConnection SQLConnection1
DataSource1 Name DataSource1
DataSet SQLDataSet1
DBEdit1 DataSource DataSource1
DataField AUTOR
DBEdit2 DataSource DataSource1
DataField TITLE
DBVavigator1 Name DBNavigator1
DataSource DataSource1
VisibleButtons.bnFirst true
VisibleButtons.bnPrior false
VisibleButtons.bnNext true
VisibleButtons.bnLast false
VisibleButtons.bnInsert false
VisibleButtons.bnDelete false
282 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 6.23 (îêîí÷àíèå)

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

VisibleButtons.bnEdit false
VisibleButtons.bnPost false
VisibleButtons.bnCancel false
VisibleButtons.bnRefresh false

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

Листинг 6.13. Обработка события FormActivate

void __fastcall TForm1::FormActivate(TObject *Sender)


{
AnsiString as;

int n; // кол-во элементов списка Params


int p;
int i;

SQLConnection1->LoadParamsFromIniFile("books.ini");

try {
SQLConnection1->Open();
SQLDataSet1->Open();

n = SQLConnection1->Params->Count;

// отобразить в строке состояния


// имя пользователя и имя базы данных
for (i = 0; i < n; i++)
{
as = WideCharToString(SQLConnection1->Params[0].Strings[i]);

p = as.Pos("User_Name");
if (p != 0 )
StatusBar1->Panels->Items[0]->Text = as;
Ãëàâà 6. Áàçû äàííûõ 283

p = as.Pos("database");
if (p != 0 )
StatusBar1->Panels->Items[1]->Text = as;
}

StatusBar1->Panels->Items[2]->Text =
"Записей: "+ IntToStr(SQLDataSet1->RecordCount);
}
catch (EDatabaseError &e)
{
StatusBar1->Panels->Items[2]->Text = e.Message;
}
}

Как было сказано ранее, компоненты dbExpress обеспечивают только одно-


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

Рис. 6.37. Форма программы работы с базой данных Книги


284 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Чтобы для просмотра данных, доступ к которым обеспечивают компоненты


dbExpress, можно было использовать DBGrid, на форму необходимо поместить
компоненты ClientDataSet и DataSetProvider. Компонент ClientDataSet
обеспечит хранение информации, полученной из базы данных, а компонент
DataSetProvider — cвязь между компонентами SQLDataSet и ClientDataSet
(информация попадает в SQLDataSet и передается в ClientDataSet). Компо-
нент DataSorce связывает компонент ClientDataSet с компонентом DBGrid.
После того как компоненты будут помещены на форму, надо выполнить их
настройку (табл. 6.24). Первым следует настроить компонент SQLConnection.
Необходимо обратить внимание на то, что значения свойств следует устанав-
ливать в том порядке, в котором они перечислены в таблице.
Модуль формы программы работы с базой данных Книги приведен в листин-
ге 6.14.

Òàáëèöà 6.24. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


SQLConnection1 Name SQLConnection1
ConnectionName books_ib
LoginPrompt false
SQLDataSet1 Name SQLDataSet1
CommandText SELECT * FROM Books
SQLConnection SQLConnection1
DataSetProvider1 Name DataSetProvider1
DataSet SQLDataSet1
ClientDataSet1 Name ClientDataSet11
ProviderName DataSetProvider1
DataSource1 Name DataSource1
DataSet ClientDataSet
DBGrid1 DataSource DataSet1
Font.Name Tahoma
Font.Size 9
Columns[0].FieldName Author
Columns[0].TitleCaption Автор
Columns[0].Title.Width 120
Ãëàâà 6. Áàçû äàííûõ 285

Òàáëèöà 6.24 (îêîí÷àíèå)

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


Columns[0].Title.Font.Style fsBold
Columns[1].FieldName Title
Columns[1].TitleCaption Название
Columns[1].Width 450
Columns[1].Title.Font.Style fsBold

Листинг 6.14. Программа работы с базой данных Книги

// начало работы программы


void __fastcall TForm1::FormActivate(TObject *Sender)
{
try
{
SQLConnection1->LoadParamsFromIniFile("books.ini");
}
catch (...) {
StatusBar1->SimpleText = "Ошибка загрузки books.ini";
}

// при попытке получить доступ к БД возможны ошибки


try
{
SQLDataSet1->Open();
ClientDataSet1->Open();
Label2->Caption =
"Наименований:" + IntToStr(ClientDataSet1->RecordCount);
}
catch (EDatabaseError &e)
{
ShowMessage("Ошибка доступа к файлу базы данных. "+ e.Message );
Edit1->Enabled = false;
Button3->Enabled = false;
DBGrid1->Enabled = false;
}
}

// Щелчок на кнопке Найти


void __fastcall TForm1::Button3Click(TObject *Sender)
286 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

{
AnsiString cmd;

cmd = "SELECT * FROM Books WHERE UPPER(Title) LIKE UPPER('%"


+Edit1->Text+"%')";

StatusBar1->SimpleText = cmd; // показать SQL-команду, направляемую


серверу

// При попытке изменить значение cвойства CommandText открытого


// набора данных (компонента SQLDataSet) возникает ошибка.
// Поэтому перед тем как изменить значение CommandText,
// надо закрыть набор данных.
SQLDataSet1->Close();
ClientDataSet1->Close();

SQLDataSet1->CommandText = cmd;

SQLDataSet1->Open();
ClientDataSet1->Open();

Label2->Caption = "Наименований:" +
IntToStr(ClientDataSet1->RecordCount);
}

Следует обратить внимание на функцию обработки события Activate. Она


загружает параметры соединения (имя сервера и псевдоним базы данных,
имя и пароль пользователя) из файла book.ini (листинг 6.15). Таким образом
пользователь, изменив значение параметров в файле book.ini, может выпол-
нить настройку программы работы с базой данных. Нетрудно заметить, что
содержимое файла book.ini — это фрагмент файла dbxconnections.ini.

Листинг 6.15. Файл инициализации соединения с сервером

[BOOKS_IB]
drivername=INTERBASE
blobsize=-1
commitretain=False
database=nk/gds_db:books
localecode=0000
Password=guest
sqldialect=3
Ãëàâà 6. Áàçû äàííûõ 287

interbase transisolation=ReadCommited
User_Name=guest
waitonlocks=True
trim char=False

Óñòàíîâêà ïðîãðàììû ðàáîòû


ñ ÁÄ íà äðóãîé êîìïüþòåð
Для того чтобы пользователь мог работать с базой данных InterBase, на его
компьютер необходимо перенести программу работы с базой данных (лучше
в отдельный каталог), драйвер, файлы инициализации драйвера и соединения.
Также на компьютере должен быть установлен клиент InterBase, а в файле
hosts должно быть указано имя и IP-адрес компьютера, на котором работает
сервер InterBase.
В качестве примера в таблице приведен список файлов, образующих прило-
жение работы с базой данных Книги (books.ib), которое для доступа к данным
использует технологию (компоненты) dbExpress. Помимо файлов, указанных
в табл. 6.25, на компьютер пользователя необходимо перенести библиотеки
cc3280mt.dll и borlndmm.dll.

Òàáëèöà 6.25. Ôàéëû, îáðàçóþùèå ïðèëîæåíèå


ðàáîòû ñ áàçîé äàííûõ InterBase Книги

Файл Комментарий

books.exe Приложение работы с базой данных Книги (скомпилировано в


режиме включения кода пакетов в exe-файл — флажок Build with
runtime packages сброшен)
dbxint30.dll Драйвер InterBase
dbxdrivers.ini Файл инициализации драйвера
books.ini Файл инициализации соединения с базой данных (фрагмент фай-
ла dbxconnections.ini)

Áàçà äàííûõ Blackfish SQL


В RAD Studio 2007 включен новый сервер баз данных Blackfish SQL Server.
Являясь полноценным SQL-сервером, он, в отличие, например, от сервера
InterBase, практически не требует администрирования, что делает использо-
вание этого сервера в информационных системах среднего уровня сложности
наилучшим решением.
288 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Óñòàíîâêà
На компьютер разработчика сервер Blackfish SQL устанавливается как служ-
ба — автоматически вместе с другими компонентами среды разработки. За-
пускается сервер также автоматически при каждом включении компьютера.
Чтобы убедиться, что сервер запущен, надо раскрыть окно Службы
(рис. 6.38) — в окне Панель управления сделать щелчок на значке Админи-
стрирование, а затем в появившемся окне Администрирование щелкнуть на
значке Службы.

Рис. 6.38. Окно Службы

Перед тем как приступить к разработке программы работы с базой данных,


рекомендуется выполнить настройку Blackfish SQL. Надо в файле конфи-
гурации bsqlserver.exe.config (по умолчанию он находится в каталоге
C:\Program Files\CodeGear\RAD Studio\5.0\bin) задать значение ключа
dataDirectory, который определяет размещение файлов базы данных. Ниже
приведен фрагмент файла конфигурации, в котором устанавливается значе-
ние ключа dataDirectory:
<add key="blackfishsql.dataDirectory" value="D:\Database" />

Äîñòóï ê ñåðâåðó
Доступ к серверу Blackfish SQL есть только у администратора. Только он мо-
жет создать базу данных и открыть ее для других пользователей. Здесь следу-
Ãëàâà 6. Áàçû äàííûõ 289

ет обратить внимание на то, что в Blackfish пользователи баз банных не реги-


стрируются на сервере так, как это делается, например, в InterBase или в
Misrosoft SQL Server. Вся информация о пользователях и их полномочиях
хранится не на сервере (в системной базе данных), а в самой базе данных.

По умолчанию сразу после установки сервера имя администратора (LoginID) —


sysdba, пароль — masterkey.

Ñîçäàíèå áàçû äàííûõ


База данных Blackfish SQL создается путем направления серверу команды
CREAE DATABASE от имени администратора. Следует обратить внимание на то,
для Blackfish SQL нет специальной утилиты, подобной, например, IBConsole
или isql для InterBase, обеспечивающей создание баз данных. Вместе с тем,
C++ Builder предоставляет возможность создания базы данных Blackfish при
помощи Data explorer.
Процесс создания базы Blackfish рассмотрим на примере базы данных Книги.
Сначала надо создать соединение с базой данных. Чтобы это сделать, нужно в
окне Data explorer щелкнуть правой кнопкой мыши в строке BLACK-
FISHSQL и в появившемся списке выбрать команду Add New Connection
(рис. 6.39, а). Затем в появившемся окне Add New Connection (рис. 6.39, б)
нужно ввести имя соединения.

Информация обо всех созданных программистом соединениях записывается в


файл c:\Documents and Settings\All Users\Документы\RAD Studio\dbExpress
\dbxconnections.ini.

После того как соединение будет создано, надо выполнить его настройку.
Сначала надо раскрыть список соединений BLACKFISHSQL, сделать щел-
чок правой кнопкой мыши на имени соединения и в появившемся списке вы-
брать команду Modify Connection (рис. 6.40).
Затем в окне Add Connection (рис. 6.41) в поле Server Name следует ввести
сетевое имя компьютера, на котором установлен Blackfish SQL (вместо име-
ни компьютера можно указать localhost). В поле Database Name нужно вве-
сти имя базы данных (если перед именем базы данных указать макрос
|DataDirectory|, то база будет создана в каталоге, имя которого указано в
файле bsqlserver.exe.config в качестве параметра ключа DataDirectory) и сде-
лать щелчок на кнопке Advanced. Затем в открывшемся окне Advanced
Properties (рис. 6.42) следует присвоить свойству create значение true,
290 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

а б
Рис. 6.39. Создание соединения с базой данных Blackfish

Рис. 6.40. Начало настройки соединения


Ãëàâà 6. Áàçû äàííûõ 291

Рис. 6.41. Окно Add Connection

Рис. 6.42. Чтобы база данных была создана,


свойству create надо присвоить значение true

закрыть окно Advanced Properties и в окне Add Connection сделать щелчок


на кнопке TestConnection. В результате этих действий будет создана база
данных Blackfish SQL, это файлы: books.jds, books_LOGA_0000000000 и
books_LOGA_ANCHOR.
292 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

После того как база данных будет создана, можно приступить к созданию
таблиц и наполнению их информацией. Делается это путем направления сер-
веру соответствующих SQL-команд.
Чтобы направить серверу SQL-команду, например, создания в базе данных
таблицы, сначала надо открыть окно SQL Window: раскрыть список соеди-
нений, сделать щелчок правой кнопкой мыши на имени соединения, обеспе-
чивающего доступ к нужной базе данных, и в появившемся списке выбрать
команду SQL Window (рис. 6.43). В результате этих действий откроется
страница Data Explorer (рис. 6.44), в нижней части которой можно набирать
SQL-команды.

Рис. 6.43. Команда SQL Window

Создание таблицы Books в базе данных Книги обеспечивает команда


CREATE TABLE BOKS ( TITLE CHAR(60) NOT NULL, AUTHOR CHAR(30) )

Процесс ее выполнения активизируется щелчком на кнопке Execute SQL


(рис. 6.45).
Ãëàâà 6. Áàçû äàííûõ 293

Рис. 6.44. Страница Data Explorer

Рис. 6.45. Кнопка Execute SQL

Äîñòóï ê áàçå äàííûõ


Изначально доступ к базе данных есть только у администратора (базу данных
Blackfish может создать только он). Чтобы у другого пользователя был дос-
туп к базе данных, он должен быть зарегистрирован как пользователь этой
базы данных. Зарегистрировать нового пользователя базы данных может
администратор или другой пользователь, обладающий правами администра-
тора.
Создание пользователя базы данных обеспечивает команда
CREATE USER UserID PASSWORD "password"

где UserID и password — идентификатор и пароль пользователя.


Например, команда
CREATE guest PASSWORD "guest"
294 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

создает пользователя guest (учетную запись пользователя), который для дос-


тупа к базе данных должен использовать пароль guest.

Ïðàâà ïîëüçîâàòåëåé
Пользователи, имеющие доступ к базе данных, могут обладать разными пра-
вами. Например, одному пользователю может быть разрешено только про-
сматривать записи, другому — просматривать и изменять, а третьему — про-
сматривать, добавлять и изменять.
Определить права доступа пользователя можно как на уровне доступа к базе
данных в целом, так и на уровне доступа к отдельным таблицам.
Определить права пользователя можно, направив серверу команду
GRANT privilege TO UserID

где privilege — список прав (табл. 6.26), UserID — идентификатор пользова-


теля.

Òàáëèöà 6.26. Ïðàâà ïîëüçîâàòåëÿ íà óðîâíå áàçû äàííûõ

Идентификатор Права
STARTUP Может открыть базу данных
WRITE Может записывать информацию в базу данных
CREATE Может создавать новые таблицы
DROP Может удалять таблицы
RENAME Может переименовывать таблицы
ADMINISTRATOR Может открыть доступ к базе данных другим пользователям
(создать пользователя), может изменить права существующих
пользователей, может выполнять другие действия (WRITE,
CREATE, DROP, RENAME), а также выполнить шифрование базы
данных

Например, команда
GRANT STARTUP TO platon

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


а команда
GRANT STARTUP, WRITE TO danila

предоставляет пользователю danila право записи в базу данных.


Ãëàâà 6. Áàçû äàííûõ 295

Команду GRANT можно использовать и для определения прав доступа к табли-


цам базы данных.
В этом случае команда выглядит так:
GRANT privileges ON table TO UserID

где:
 privileges — список прав (SQL-команд, которые пользователь может на-
править серверу);
 table — таблица базы данных;
 UserID — идентификатор пользователя.
Например, команды, определяющие полномочия доступа пользователей
platon и danila к таблице books могут быть такими:

GRANT SELECT ON books TO platon


GRANT SELECT, INSER, UPDATE ON books TO danila
Как видно, пользователь platon может только просматривать таблицу books, а
пользователь danila — просматривать, добавлять и редактировать информа-
цию.
Если нескольким пользователям надо предоставить одинаковые права, то
вместо того чтобы определять права каждого из них, можно определить (соз-
дать) роль, указать права для этой роли, а затем назначить роль пользовате-
лям.
Команда, обеспечивающая создание роли, в общем виде выглядит так:
CREATE ROLE RoleID

где RoleID — идентификатор роли.


Пример:
CREATE ROLE operator

Команда определения полномочий роли идентична команде определения


полномочий пользователя.
Например, команда
GRANT SELECT, INSER, UPDATE ON books TO operator

определяет полномочия роли operator.


После того как роль определена, ее можно назначить конкретному пользова-
телю.
Команда назначения роли пользователю в общем виде выглядит так:
GRANT RoleID TO UserID
296 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Например, команда
GRANT operator TO larisa

предоставляет пользователю larisa полномочия в соответствии с ролью


operator.
В каждой базе данных по умолчанию определена роль PUBLIC. Эта роль обыч-
но используется для обеспечения доступа к таблицам базы данных в режиме
"только просмотр". Особенность этой роли заключается в том, что она авто-
матически назначается пользователям, полномочия которых явно (при помо-
щи команды GRANT) не заданы. Следует обратить внимание на то, что полно-
мочия роли PUBLIC по умолчанию не определены, их надо указывать явно.
Например, команда
GRANT SELECT ON books TO PUBLIC

открывает таблицу books для просмотра всем пользователям базы данных,


права которых не указаны явно.

Áàçà äàííûõ Êíèãè


Процесс разработки приложения работы с базой данных Blackfish SQL рас-
смотрим на примере программы, обеспечивающей работу с базой данных
Книги.
Сначала надо создать базу данных Книги (books), поместить в нее таблицу
books, записать в таблицу данные, открыть доступ к базе данных пользовате-
лю gust и определить его полномочия. Характеристики полей таблицы books
приведены в табл. 6.27, SQL-команды, обеспечивающие выполнение описан-
ной выше задачи, — в листинге. 6.16.

Òàáëèöà 6.27. Ïîëÿ òaáëèöû books

Поле Тип Примечание


TITLE CHAR(60) Обязательное поле
AUTHOR CHAR(25)

Листинг. 6.16. Создание таблицы и определение полномочий


пользователя guest

CREATE TABLE BOOKS(TITLE CHAR(60) NOT NULL, AUTHOR CHAR(30))


INSERT INTO BOOKS(TITLE,AUTHOR)
VALUES("C# в примерах и задачах", "Культин Н.Б.")
Ãëàâà 6. Áàçû äàííûõ 297

INSERT INTO BOOKS(TITLE,AUTHOR)


VALUES("Delphi в примерах и задачах", "Культин Н.Б.")
CREATE USER guest PASSWORD "guest"
GRANT SELECT ON BOOKS TO public

Доступ к базе данных Blackfish SQL обеспечивают компоненты dbExpress


(рис. 6.46). Следует обратить внимание на то, что архитектура DBEXPRESS
реализует только однонаправленный (unidirectional) доступ к данным. Это
значит, что указатель текущей записи может перемещаться в одном направ-
лении (только к следующей записи). Именно поэтому для просмотра данных
в режиме таблицы использовать компонент DataGrid напрямую нельзя: при
попытке переместить указатель текущей строки (записи) в предыдущую
строку возникает исключение. Чтобы обеспечить возможность просмотра
данных в режиме таблицы, в форму приложения необходимо добавить ком-
понент ClientDataSet, который обеспечит буферизацию данных и возмож-
ность перемещения указателя текущей записи "назад" (к предыдущей записи).

Рис. 6.46. Компоненты dbExpress

Форма программы работы с базой данных Книги приведена на рис. 6.47. Ком-
понент SQLConnection обеспечивает соединение с базой данных, компонент
SQLDataSet — хранит информацию, полученную в результате выполнения за-
проса (команды SELECT). Компонент ClientDataSet используется в качестве
буфера, он хранит информацию, полученную из базы данных, и обеспечивает
возможность ее просмотра при помощи компонента DataGrid. Связь между
компонентами SQLDataSet и ClientDataSet обеспечивает компонент
DataSetProvider, между компонентами ClientDataSet и DBGrid — компонент
DataSourse.
298 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 6.47. Форма программы работы с базой данных Книги

Значения свойств компонентов приведены в табл. 6.28. Необходимо обратить


внимание на то, что значения следует устанавливать в том порядке, в котором
они приведены в тaблице.

Òàáëèöà 6.28. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ

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


SQLConnection1 Name SQLConnection1
ConnectionName books
LoginPrompt false
LoadParamsOnConnect true
SQLDataSet1 Name SQLDataSet1
CommandText SELECT * FROM BOOKS
SQLConnection SQLConnection1
DataSetProvider1 Name DataSetProvider1
DataSet SQLDataSet1
ClientDataSet11 Name ClientDataSet11
ProviderName DataSetProvider1
Ãëàâà 6. Áàçû äàííûõ 299

Òàáëèöà 6.28 (îêîí÷àíèå)

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


DataSource1 Name DataSource1
DataSet ClientDataSet
Font.Name Tahoma
Font.Size 9
DataGrid1 Name DataGrid1
DataSource DataSource1
ReadOnly true
Columns[0].FieldName AUTHOR
Columns[0].TitleCaption Автор
Columns[0].Title.Width 120
Columns[0].Title.Font.Style fsBold
Columns[1].FieldName TITLE
Columns[1].TitleCaption Название
Columns[1].Width 450
Columns[1].Title.Font.Style fsBold

Процедуры обработки событий приведены в листинге 6.17.

Листинг 6.17. Процедуры обработки событий

void __fastcall TForm1::FormActivate(TObject *Sender)


{
AnsiString as;

int n; // кол-во элементов списка Params


int p;
int i;

n = SQLConnection1->Params->Count;

// отобразить имя пользователя и имя базы данных


// в строке состояния
for (i = 0; i < n; i++) {
as = WideCharToString(SQLConnection1->Params[0].Strings[i]);
300 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

p = as.Pos("user_name");
if (p != 0 ) {
StatusBar1->Panels->Items[0]->Text = as;
}

p = as.Pos("Database");
if (p != 0 ) {
StatusBar1->Panels->Items[1]->Text = as;
}
}

try
{
SQLConnection1->Open();
ClientDataSet1->Open();
}
catch ( TDBXError &e)
{
ShowMessage(e.Message);
}
}

// щелчок на кнопке Найти


void __fastcall TForm1::Button1Click(TObject *Sender)
{

AnsiString cmd;

cmd = "SELECT * FROM BOOKS WHERE UPPER(TITLE) like UPPER('%"+


Edit1->Text + "%')";

// ShowMessage(cmd);

/*
При попытке изменить значение свойства CommandText
открытого набора данных возникает исключение,
поэтому перед тем как изменить значение свойства
CommandText, закроем набор данных. }
*/

SQLConnection1->Close();
ClientDataSet1->Close();
Ãëàâà 6. Áàçû äàííûõ 301

SQLDataSet1->CommandText = cmd;

SQLConnection1->Open();
ClientDataSet1->Open();

if ( SQLDataSet1->RecordCount == 0 )
{
ShowMessage("В БД нет записей, удовлетворяющих критерию запроса");
}
}

// нажатие клавиши в поле Edit1


void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
if ( Key == VK_RETURN) {
Button1->SetFocus();
}
}

Как было сказано ранее, параметры соединений, созданных программистом,


находятся в файле dbxconnections.ini, из которого они (при настройке компо-
нента SQLConnection) копируются в свойство Params. Если вы предполагаете,
что программа работы с базой данных будет устанавливаться на другие ком-
пьютеры, то свойству LoadParamsOnConnect компонента SQLConnction следует
присвоить значение true. В этом случае параметры соединения будут загру-
жаться из файла dbxconnections.ini при каждом открытии соединения. Такое
решение позволяет выполнять настройку программы работы с базой данных
путем внесения изменений в файл конфигурации dbxconnections.ini.

Ðàçâåðòûâàíèå ïðèëîæåíèÿ ðàáîòû


ñ áàçîé äàííûõ
Óñòàíîâêà è íàñòðîéêà Blackfish SQL

Сервер Blackfish SQL является .NET-приложением. Поэтому перед тем как при-
ступить к установке Blackfish SQL, необходимо убедиться в том, что на том ком-
пьютере, куда устанавливается Blackfish SQL, есть Microsoft .NET Framework
(на компьютер программиста Microsoft .NET Framework устанавливается авто-
матически вместе с RAD Studio). Следует обратить внимание: платформа
Microsoft .NET Framework используется многими современными программами и,
вполне вероятно, что на компьютере пользователя она уже установлена
(Microsoft .NET Framework гарантированно есть на компьютере с Windows Vista).
302 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Сначала на диск сервера (лучше в отдельный каталог) надо скопировать файлы


BSQLServer.exe, Borland.Data.Blackfish.LocalClient.dll, BSQLServer.exe.config,
а также файл лицензии (slip-файл). После этого следует определить каталог,
предназначенный для файлов баз данных. Имя этого каталога надо указать в
файле конфигурации сервера (BSQLServer.exe.config) в качестве значения
ключа blackfisfsql.dataDirectory, например:
<add key="blackfishsql.dataDirectory" value="D:\Database" />

Следует обратить внимание: если значение ключа blackfisfsql.dataDirectory


задано, то в строке Database файла параметров соединения вместо пути к ба-
зе данных можно указать макроc — строку |DataDirectory|.
Например:
database=|Datadirectory|books

После этого надо открыть окно командной строки и набрать команду


Каталог\BSQLServer.exe –install,

Рис. 6.48. Настройка брандмауэра


Ãëàâà 6. Áàçû äàííûõ 303

где Каталог — имя каталога, в котором находятся файлы Blackfish SQL


Server. Эта команда обеспечивает установку Blackfish SQL Server как службу.
Теперь при каждом включении сервера (компьютера) Blackfish SQL будет
запускаться автоматически.

При установке службы Blackfish SQL Server в Windows Vista окно командной
строки надо открыть от имени администратора.

Брандмауэр Windows по умолчанию блокирует доступ клиента (программы ра-


боты с базой данных) к серверу. Поэтому после установки Blackfish SQL Server
необходимо в список исключений брандмауэра добавить ссылку на BSQLServer
(рис. 6.48).

Óñòàíîâêà ïðîãðàììû ðàáîòû ñ áàçîé äàííûõ


Процесс установки программы работы с базой данных Blackfish на компью-
тер пользователя состоит из двух шагов. Сначала на диск компьютера (лучше
в отдельный каталог) надо скопировать файлы, образующие программу рабо-
ты с базой данных (список файлов программы работы с базой данных Книги
приведен в табл. 6.29). Помимо файлов, указанных в табл. 6.29, на компьютер
пользователя необходимо перенести библиотеки cc3280mt.dll и borlndmm.dll.

Òàáëèöà 6.29. Ôàéëû ïðîãðàììû ðàáîòû ñ áàçîé äàííûõ Книги

Файл Комментарий

books.exe Приложение работы с базой данных Blackfish Книги (ском-


пилировано в режиме включения кода пакетов в exe-
файл — флажок Build with runtime packages сброшен)
dbxint30.dll Драйвер
dbxdrivers.ini Файл инициализации драйвера
dbxconnections.ini Файл инициализации соединения с базой данных (парамет-
ры соединения находятся в секции books)

Затем надо выполнить настройку программы — в файле параметров соедине-


ния dbxconnections.ini указать сетевое имя сервера. Кроме этого, в файл
C:\Windows\System32\drivers\ets\hosts клиента надо записать имя и IP-адрес
сервера (узнать IP-адрес сервера можно, запустив на сервере утилиту
ipconfig.exe).
304 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

В качестве примера в листинге 6.18 приведен фрагмент файла


dbxconnections.ini, а в листинге 6.19 — файла hosts. Обратите внимание:
в файле конфигурации указан макрос |DataDirectory|, поэтому в фай-
ле BSQLServer.exe.config должно быть указано значения ключа
blackfisfsql.dataDirectory.

Листинг 6.18. Параметры соединения с базой данных Книги (dbxconnections.ini)

[books]
drivername=BLACKFISHSQL
User_Name=guest
port=2508
create=false
readonlydb=false
HostName=Danila
Database=|DataDirectory|books

Листинг 6.19. Файл hosts

127.0.0.1 localhost
192.168.1.5 nk
ÃËÀÂÀ 7

Êîìïîíåíò ïðîãðàììèñòà

Программист может создать свой компонент, поместить его на одну из вкла-


док палитры компонентов и использовать при разработке приложений точно
так же, как и другие компоненты C++ Builder.
Наиболее просто новый компонент можно создать на базе уже существующе-
го путем расширения его возможностей. Например, компонент, обеспечи-
вающий ввод и редактирование числа, можно создать на основе компонента
Edit.
Процесс создания компонента рассмотрим на примере. Создадим компонент,
назовем его NkEdit, внешне он ничем не будет отличаться от стандартного
компонента Edit, но в его поле можно будет ввести только число. В качестве
базы выберем компонент Edit. Очевидно, что у нового компонента должны
быть и новые свойства (табл. 7.1), обеспечивающие решение поставленной
задачи. Так как новый компонент создается на базе существующего, то он
наследует все свойства и методы базового компонента.

Òàáëèöà 7.1. Ñâîéñòâà êîìïîíåíòà NkEdit

Свойство Тип Описание


OnlyPositive bool Определяет вид числа, которое можно вве-
сти в поле редактирования. Если значение
свойства равно true, то в поле компонента
можно ввести только положительное число.
По умолчанию значение свойства равно
true
MaxLenInt int Максимальное количество цифр целой час-
ти числа, которое можно ввести в поле ре-
дактирования. По умолчанию значение
свойства равно 6
306 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 7.1 (îêîí÷àíèå)

Свойство Тип Описание

MaxLenFrac int Максимальное количество цифр дробной


части числа, которое можно ввести в поле
редактирования. По умолчанию значение
свойства равно 2
Value float Значение, соответствующее строке, кото-
рая находится в поле редактирования
Next TWinControl * Компонент, на который будет установлен
фокус в результате нажатия в поле редак-
тирования клавиши <Enter>

Приступая к разработке нового компонента, следует четко сформулировать


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

Ìîäóëü êîìïîíåíòà
Чтобы начать работу над новым компонентом, надо в меню Component вы-
брать команду New VCL Component, затем, в появившемся окне, — базовый
класс (рис. 7.1). Компонент NkEdit разрабатывается на основе стандартного
компонента Edit. Поэтому в качестве базового класса следует выбрать класс
TEdit. В следующем окне (рис. 7.2), которое становится доступным в резуль-
тате щелчка на кнопке Next, надо задать имя класса (поле Class Name), имя
вкладки (поле Palette Page), на которую предполагается установить компо-
нент, и имя модуля (поле Unit name) создаваемого компонента. Следует об-
ратить внимание, что согласно принятому среди программистов соглашению,
имя класса должно начинаться с буквы T. Обратите внимание: если в поле
Palette Page указано имя несуществующей вкладки палитры компонентов, то
во время установки компонента вкладка с указанным именем будет создана.
В результате щелчка на кнопке Finish будет сформирован шаблон модуля
компонента NkEdit — файлы NkEdit.h (листинг 7.1) и NkEdit.cpp (лис-
тинг 7.2).
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 307

Рис. 7.1. Сначала надо выбрать базовый класс для нового компонента

Рис. 7.2. В поле Unit name надо ввести имя модуля создаваемого компонента
308 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Листинг 7.1. Шаблон модуля компонента NkEdit (NkEdit.h)

#ifndef NkEditH
#define NkEditH
//----------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
//----------------------------------------------------------------------
class PACKAGE TNkEdit : public TEdit
{
private:
protected:
public:
__fastcall TNkEdit(TComponent* Owner);
__published:
};
//----------------------------------------------------------------------
#endif

Листинг 7.2. Шаблон модуля компонента NkEdit (NkEdit.cpp)

#include <vcl.h>

#pragma hdrstop

#include "NkEdit.h"
#pragma package(smart_init)
//----------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created
// do not have any pure virtual functions.
//

static inline void ValidCtrCheck(TNkEdit *)


{
new TNkEdit(NULL);
}
//----------------------------------------------------------------------
__fastcall TNkEdit::TNkEdit(TComponent* Owner)
: TEdit(Owner)
{
}
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 309

//----------------------------------------------------------------------
namespace Nkedit
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TNkEdit)};
RegisterComponents("Kultin", classes, 0);
}
}
//----------------------------------------------------------------------

В файл NkEdit.h нужно добавить объявления полей, функций, обеспечиваю-


щих доступ к полям, а также объявления новых методов (см. тaбл. 7.1). Так
как реакция компонента NkEdit на событие KeyPress должна быть иной, чем
у базового компонента, то в объявление класса TNkEdit необходимо помес-
тить объявление функции KeyPress, которая обеспечит обработку этого со-
бытия. Функции, объявленные в h-файле, надо поместить в файл NkEdit.cpp.
Файлы NkEdit.h и NkEdit.cpp после внесения всех необходимых дополнений
приведены в листингах 7.3 и 7.4.

Листинг 7.3. Модуль компонента NkEdit (NkEdit.h)

// Компонент NkEdit
// (c) Культин Н.Б., 2006 — 2008

#ifndef NkEditH
#define NkEditH

//----------------------------------------------------------------------

#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>

//----------------------------------------------------------------------

class PACKAGE TNkEdit : public TEdit


{
private:
// поля хранят значения свойств
bool FOnlyPositive; // true — отрицательное число ввести нельзя
310 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

int FMaxLenInt; // допустимое кол-во цифр целой части


int FMaxLenFrac; // допустимое количество цифр дробной части
TWinControl *FNext; // компонент, на который будет установлен
// фокус в результате нажатия <Enter>

// функции используются для вычисления и установки значений свойств


void __fastcall SetValue( float value); // устанавливает значение
// свойства
float __fastcall GetValue(void); // возвращает значение
// свойства
protected:

public:
// конструктор
virtual __fastcall TNkEdit(TComponent* Owner);

// Cвойства, объявленные в секции public,


// в Object Inspector не отображаются
__property float Value = { read = GetValue, write = SetValue };

__published:
// Свойства, объявленные в секции published,
// отображаются в Object Inspector
__property bool OnlyPositive = {
read = FOnlyPositive,
write = FOnlyPositive,
default = true };

__property int MaxLenInt = {


read = FMaxLenInt,
write = FMaxLenInt,
default = 6 };

__property int MaxLenFrac = {


read = FMaxLenFrac,
write = FMaxLenFrac,
default = 2 };

__property TWinControl * Next = {


read = FNext,
write = FNext };
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 311

// функция обработки события KeyPress


DYNAMIC void __fastcall KeyPress( char & Key);
};

#endif

Листинг 7.4. Модуль компонента NkEdit (NkEdit.cpp)

// Компонент NkEdit
// (c) Культин Н.Б., 2006

#include <vcl.h>

#pragma hdrstop

#include "NkEdit.h"

#pragma package(smart_init)

//--------------------------------------------------------------------
// ValidCtrCheck is used to assure that the components created
// do not have any pure virtual functions.
//

static inline void ValidCtrCheck(TNkEdit *)


{
new TNkEdit(NULL);
}

//---- Конструктор ---------------------------------------------------


__fastcall TNkEdit::TNkEdit(TComponent* Owner)
: TEdit(Owner)
{
// конструктор имеет прямой доступ к полям
FOnlyPositive = true;
FMaxLenInt = 6;
FMaxLenFrac = 2;
FNext = NULL;

// свойство Text унаследовано от базового класса TEdit


Text = "0";
}
312 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

namespace Nkedit
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TNkEdit)};
RegisterComponents("Kultin", classes, 0);
}
}

// возвращает значение свойства Value


float __fastcall TNkEdit::GetValue()
{
if ( Text.Length() )
return StrToFloat(Text);
else
return 0;
}

// устанавливает значение свойства Value


void __fastcall TNkEdit::SetValue( float v)
{
Text = FloatToStr(v);
Update();
}

// обработка события KeyPress в поле компонента NkEdit


void __fastcall TNkEdit::KeyPress(char &Key)
{
int p; // позиция DecimalSeparator
int n; // кол-во введенных цифр

// коды запрещенных клавиш заменим нулем

switch ( Key ) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 313

case '9':
p = Text.Pos(DecimalSeparator);
if (( p == 0) || ( SelStart < p)) {
// Цифра целой части.
// Определим, сколько цифр уже введено
if (p == 0) {
n = Text.Length();
}
else n = p-1;
if ((n > 0) && (Text[1] == '-')){
n--;
}
if ( n == FMaxLenInt) {
Key = 0;
}
}
else {
// цифра дробной части
n = Text.Length()- p;
if (n == FMaxLenFrac) {
Key = 0;
}
}

break;

case '.':
case ',':
Key = DecimalSeparator;
if ((FMaxLenFrac == 0 ) ||(Text.Pos(DecimalSeparator) !=0 ))
{
Key = 0;
}
break;

case '-':
if ((FOnlyPositive) || (SelStart > 0))
{
Key = 0;
}
break;

case VK_RETURN:
if ( Next != NULL)
314 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

{
FNext->SetFocus();
}

case VK_BACK:
break;

default: // остальные символы


Key = 0;
}

// Не забыть вызвать функцию обработки события


// KeyPress базового класса !!!
TEdit::KeyPress(Key);
}

В секции private класса TNkEdit объявлены поля (имена полей, согласно


принятому соглашению, начинаются с буквы F, от англ. Field — поле). Поля
хранят характеристики компонента:
 FOnlyPositive — вид числа (положительное или отрицательное), которое
можно ввести в поле редактирования;
 FMaxLenInt — максимально допустимое число цифр целой части числа;
 FMaxLenFrac — максимально допустимое число цифр дробной части числа;
 FNext — компонент, на который будет установлен фокус (перемещен кур-
сор, если компонент является полем редактирования) в результате нажа-
тия клавиши <Enter>.
Следует обратить внимание, что поля объявлены в секции private, поэтому у
программ, которые будут использовать компонент NkEdit, доступа к ним не
будет.
Уникальные свойства компонента NkEdit (т. е. свойства, которых нет у базо-
вого компонента) объявлены в секциях public и published. Свойства, объ-
явленные в секции published, отображаются в окне Object Inspector, а объ-
явленные в секции public — нет.
В объявлении свойства указывается тип свойства и функции, которые уста-
навливают (write) и возвращают (read) значение свойства.
Так объявление
__property float Value = { read = GetValue, write = SetValue };

показывает, что свойство Value доступно как для чтения, так и для записи, а
также что значение свойства устанавливает функция SetValue, а функция
GetValue возвращает значение свойства.
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 315

В случае, если значение свойства хранит поле, в объявлении свойства (после


read и write) указывается имя соответствующего поля (см. объявления
остальных свойств).
Функция KeyPress обеспечивает обработку соответствующего события в по-
ле компонента, она подменяет соответствующую функцию базового компо-
нента.
Необходимо обратить внимание на то, что модуль компонента не является
программой, поэтому выполнить его компиляцию нельзя (команда
ProjectCompile недоступна).

Òåñòèðîâàíèå êîìïîíåíòà
После того как будет создан модуль компонента, необходимо убедиться, что
компонент работает правильно. Для этого надо создать приложение, которое
будет использовать созданный компонент.
В палитре компонентов отображаются те, которые находятся в каком-либо из
пакетов компонентов. Так как компонент NkEdit пока еще не включен ни в
один из пакетов, то его значок в палитре не отображается, и следовательно,
поместить компонент NkEdit на форму привычным способом нельзя. Поэтому
тестируемый компонент придется создать в коде — поместить в текст про-
граммы инструкции, обеспечивающие создание и настройку компонента.
Приложение, обеспечивающее тестирование компонента NkEdit, создается
следующим образом. Сначала надо активизировать процесс создания нового
VCL Forms-приложения и настроить форму. После того как форма будет го-
това, необходимо сохранить проект в том каталоге, в котором находится мо-
дуль тестируемого компонента.
Форма программы тестирования компонента NkEdit приведена на рис. 7.3.
Она содержит три компонента Label и командную кнопку. Поля ввода дан-
ных (два компонента NkEdit) будут созданы во время работы программы.
После того как проект будет сохранен, в h-файл модуля формы надо помес-
тить директиву #include "NkEdit.h", а в секцию private объявления класса
формы — объявление двух объектов (компонентов) NkEdit (листинг 7.5). За-
тем в конструктор формы надо добавить инструкции, обеспечивающие соз-
дание и инициализацию компонентов NkEdit (листинг 7.6). Следует обратить
внимание на то, что в инструкции создания компонента в качестве параметра
необходимо указать объект, на который должен быть помещен компонент
(этот же объект надо указать в качестве значения свойства Parent).
316 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 7.3. Форма программы тестирования компонента NkEdit


(поля ввода данных будут созданы во время работы программы)

Листинг 7.5. Программа тестирования компонента NkEdit (Unit1.h)

#ifndef Unit1H
#define Unit1H
//----------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>

#include "NkEdit.h"

class TForm1 : public TForm


{
__published: // IDE-managed Components
TLabel *Label1;
TLabel *Label2;
TButton *Button1;
TLabel *Label3;
void __fastcall Button1Click(TObject *Sender);

private: // User declarations


TNkEdit *NkEdit1; // поле Цена — компонент NkEdit
TNkEdit *NkEdit2; // поле Курс — компонент NkEdit

public: // User declarations


__fastcall TForm1(TComponent* Owner);
};

extern PACKAGE TForm1 *Form1;

#endif
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 317

Листинг 7.6. Программа тестирования компонента NkEdit (Unit1.cpp)

/ Тест компонента NkEdit


// Пересчет цены из долларов в рубли.
// В качестве полей ввода исходных данных используются
// компоненты NkEdit
// (c) Культин Н.Б., 2006 — 2008

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//----------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

// Конструктор ----------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
// создадим и инициализируем компоненты NkEdit
NkEdit1 = new TNkEdit(Form1);
NkEdit2 = new TNkEdit(Form1);

// настройка поля Цена


NkEdit1->Left = 60;
NkEdit1->Top = 20;
NkEdit1->TabOrder = 0;
NkEdit1->Parent = Form1;

NkEdit1->MaxLenFrac = 0;
NkEdit1->Next = NkEdit2;

// настройка поля Курс


NkEdit2->Left = 60;
NkEdit2->Top = 52;
NkEdit2->TabOrder = 1;
NkEdit2->Parent = Form1;

NkEdit2->MaxLenInt = 2;
NkEdit2->Next = Button1;
318 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Button1->TabOrder = 3;
}

// щелчок на кнопке OK
void __fastcall TForm1::Button1Click(TObject *Sender)
{
float usd,k,rub;

usd = NkEdit1->Value;
k = NkEdit2->Value;

rub = usd * k;

Label3->Caption = FloatToStr(usd) + "$ = " +


FloatToStrF(rub,ffCurrency,6,2);
}

Перед тем как выполнить компиляцию, в проект надо добавить модуль ком-
понента (в меню Project выбрать команду Add to Project и в открывшемся
окне выбрать файл NkEdit.cpp).
После этого можно выполнить компиляцию (команда Project Build), за-
пустить программу и убедиться, что созданный компонент работает так, как
нужно (рис. 7.4).

Рис. 7.4. Тестирование компонента: поля ввода данных — компоненты NkEdit

Ïàêåò êîìïîíåíòîâ
Пакет компонентов — это динамическая библиотека, в которой находится код,
реализующий функциональность компонентов.
Различают пакеты времени разработки (Design time package) и пакеты времени
выполнения (Runtime package). Пакеты времени разработки используются сре-
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 319

дой разработки, пакеты времени выполнения — программами, использующи-


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

Ñîçäàíèå ïàêåòà
Программист может создать пакет, поместить в него свои компоненты и за-
тем установить созданный пакет.
Чтобы создать пакет, надо в меню File выбрать команду NewPackage. Ре-
зультат выполнения команды отображается в окне Project Manager
(рис. 7.5). Созданный пакет рекомендуется сразу сохранить в том каталоге, в
котором находится модуль компонента, который предполагается поместить в
пакет.

Рис. 7.5. Структура пакета отображается в окне Project Manager


320 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

После того как пакет будет сохранен, в него надо поместить компонент —
выбрать в меню Project команду Add to Project и в появившемся окне в поле
Unit file name ввести имя модуля компонента (рис. 7.6). Компонент будет
добавлен в пакет, что отразится в окне Object Inspector.

Рис. 7.6. В поле Unit file name надо ввести имя файла модуля компонента

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


тов используется значок компонента базового класса. Если программист хо-
чет, чтобы значок созданного им компонента выглядел иначе, он должен до-
бавить в пакет файл ресурса, содержащий битовый образ значка компонента.
Имя файла ресурса должно совпадать с именем модуля компонента, а имя
битового образа — с именем класса компонента. Например, файл ресурсов
для компонента NkEdit должен иметь имя nkedit, а битовый образ — TNKEDIT.
Создать файл ресурсов компонента можно, например, в Borland Image Editor.
Процесс создания файла ресурсов (res-файла) начинается выбором в меню
File команды New Resource File (рис. 7.7). Чтобы добавить в файл ресур-
сов битовый образ, надо в меню Resource выбрать команду NewBitmap и в
появившемся окне Bitmap Properties (рис. 7.8) задать характеристики бито-
вого образа: размер 24×24, 16 или 256 цветов.
Рекомендуется сразу изменить имя ресурса — выбрать в меню Resource
команду Rename и ввести нужный идентификатор (для компонента NkEdit —
TNKEDIT).
После того как значок будет нарисован (чтобы раскрыть окно графического
редактора, сделайте двойной щелчок на имени ресурса), надо сохранить файл
ресурсов в том каталоге, в котором находится модуль компонента.
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 321

Рис. 7.7. Чтобы создать файл ресурсов компонента,


надо в меню File выбрать команду New Resource File

Рис. 7.8. Характеристики битового образа Рис. 7.9. Структура проекта создания пакета
значка компонента NkPackage
322 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

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


тов — в меню Project выбрать команду Add to Project и в появившемся окне
указать файл ресурсов.
В качестве примера на рис. 7.9 приведена структура проекта создания пакета
NkPackage.

Êîìïèëÿöèÿ ïàêåòà
После того как пакет будет готов, можно выполнить его компиляцию — вы-
брать в меню Project команду Build. В результате компиляции в каталоге
C:\Documents and Settings\All Users\Documents\RAD Studio\5.0 будет создан
пакет компонентов (bpl-, pbi- и lib-файлы).
По умолчанию создается универсальный пакет. Тип создаваемого пакета
можно задать на вкладке Description окна Project Options, выбрав соответст-
вующий переключатель (рис. 7.10).

Рис. 7.10. Информация о пакете отображается на вкладке Description


Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 323

Следует обратить внимание на то, что в процессе компиляции пакета, помимо


динамической библиотеки (вспомните, пакет — это специальная динамиче-
ская библиотека), создается соответствующая статическая библиотека (lib-
файл). Эта библиотека будет использоваться при компиляции проекта в слу-
чае, если будет задан режим компиляции Without runtime packages (сброшен
находящийся на вкладке Project Options: Packages флажок Build with
runtime packages).

Óñòàíîâêà ïàêåòà
Для того чтобы программист мог использовать созданный компонент, необ-
ходимо установить пакет, в котором он находится.
Чтобы установить пакет, надо в меню Component выбрать команду Install
Packages, в появившемся окне (рис. 7.11) сделать щелчок на кнопке Add и
затем в следующем окне выбрать пакет (bpl-файл).

Рис. 7.11. Чтобы установить пакет, надо нажать кнопку Add

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


которой указано в функции Register (см. листинг 7.4), появится значок ком-
понента. Следует обратить внимание, что палитра компонентов доступна
только в режиме конструирования формы. Поэтому, чтобы убедиться, что
компонент, находящийся в установленном пакете, стал доступен, надо акти-
324 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

визировать процесс создания нового приложения. Если процесс установки


был выполнен верно, то в палитре компонентов на вкладке, которая указана в
процедуре регистрации компонента, должен появиться значок компонента
(рис. 7.12).

Рис. 7.12. Значок компонента NkEdit отображается на вкладке Kultin

Òåñòèðîâàíèå êîìïîíåíòà
После того как значок компонента NkEdit появится в палитре компонентов,
необходимо проверить поведение компонента во время разработки приложе-
ния (работоспособность компонента была проверена раньше, когда компо-
нент добавлялся на форму приложения динамически, во время работы про-
граммы).
Можно считать, что компонент работает правильно, если во время разработ-
ки приложения удалось его поместить на форму и, используя Object
Inspector, установить значения свойств, причем как новых, так и унаследо-
ванных от родительского класса.
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 325

Работоспособность компонента NkEdit можно проверить, применив его, на-


пример, в программе Доход. Форма приложения приведена на рис. 7.13.
После того как в форму будут добавлены компоненты, следует, используя
Object Inspector (рис. 7.14), выполнить их настройку (табл. 7.2), после чего
можно приступить к созданию процедур обработки событий.

Рис. 7.13. Форма программы Доход (тест компонента NkEdit)

Рис. 7.14. Значения свойств MaxLenFrac, MaxLenInt, Next, OnlyPositive


компонента NkEdit можно задать в окне Object Inspector
326 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Òàáëèöà 7.2. Çíà÷åíèÿ ñâîéñòâ êîìïîíåíòîâ NkEdit

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


NkEdit1 OnlyPositive true
MaxLenInt 5
MaxLenFrac 0
Next NkEdit2
NkEdit2 OnlyPositive true
MaxLenInt 2
MaxLenFrac 2
Next Button1

Текст программы Конвертор приведен в листинге 7.7. Здесь надо обратить


внимание на следующее:
 в программе нет кода, обеспечивающего фильтрацию символов, вводимых
в поля редактирования. Тем не менее, во время работы программы пользо-
ватель сможет ввести в поля редактирования только положительные чис-
ла, причем в поле Цена (NkEdit1) можно ввести целое число, не превы-
шающее 99999, а в поле Курс (NkEdit2) — дробное, не большее 99,99;
 в программе для преобразования строки (находящейся в поле редактиро-
вания компонента NkEdit) в число не используется функция StrToFloat.
Число, которое соответствует строке символов, введенной в поле редакти-
рования, получается путем обращения к свойству Value;
 в программе нет процедур обработки событий KeyPress, тем не менее
в результате нажатия клавиши <Enter> фокус (курсор) перемещается на
нужный компонент.
Таким образом, использование компонента NkEdit вместо стандартного Edit
освобождает программиста от рутины, сокращает размер кода, делает его бо-
лее понятным.

Листинг 7.7. Программа Конвертор (тест компонента NkEdit)

// щелчок на кнопке OK
void __fastcall TForm1::Button1Click(TObject *Sender)
{
float usd,k,rub;

usd = NkEdit1->Value;
k = NkEdit2->Value;

rub = usd * k;
Ãëàâà 7. Êîìïîíåíò ïðîãðàììèñòà 327

Label3->Caption = FloatToStr(usd) + "$ = " +


FloatToStrF(rub, ffCurrency, 6, 2);
}

Óñòàíîâêà ïðîãðàììû íà äðóãîé êîìïüþòåð


Как было сказано ранее, программе, скомпилированной в режиме использо-
вания пакетов, необходимы пакеты, в которых находятся применяемые ею
компоненты. В общем случае это vcl100.bpl и rtl100.bpl. Если программа ис-
пользует компонент, созданный программистом, то помимо стандартных па-
кетов, ей должен быть доступен и тот, в котором этот компонент находится.
Поэтому в установочный комплект, помимо exe-файла и файлов стандартных
библиотек, надо включить пакет, в котором находится компонент програм-
миста (bpl-файл). На компьютере пользователя стандартные библиотеки
(vcl100.bpl, rtl100.bpl) можно поместить в системный каталог
С:\Windows\system32 или в тот каталог, где находится программа (поскольку
она ищет необходимые ей библиотеки сначала в том каталоге, из которого
она запущена, а затем уже в системном каталоге), а библиотеку, в которой
находится компонент, нужно поместить в каталог программы. Следует обра-
тить внимание на то, что разработчик может существенно облегчить себе
жизнь, если включит весь код, необходимый для работы программы, в exe-
файл. Для этого перед тем как выполнить компиляцию, надо в окне Project
Options, которое становится доступным в результате выбора в меню Project
команды Options, на вкладках Packages и Linking сбросить, соответственно,
флажки Build with runtime packages и Dynamic RTL.

Ðàñïðîñòðàíåíèå êîìïîíåíòà
Созданный компонент (пакет компонентов) можно передать своим коллегам-
программистам, для того чтобы они могли использовать его в своих разра-
ботках. В установочный комплект надо включить bpl-, bpi- и lib-файлы, а
также h-файл, содержащий объявление класса компонента. На компьютере
разработчика эти файлы надо поместить в каталог среды разработки:
 bpl-файл — в подкаталог bin;
 bpi- и lib-файлы — в подкаталог lib;
 h-файл — в подкаталог include.
Необходимо отметить, что при попытке открыть проект, в котором исполь-
зуются недоступные компоненты, среда разработки выводит сообщение о
том, что компонент недоступен. В этом случае рекомендуется прервать за-
грузку проекта, установить пакет, в котором находится компонент, и только
после этого сделать попытку еще раз открыть проект.
ÃËÀÂÀ 8

Ñïðàâî÷íàÿ èíôîðìàöèÿ

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


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

Ñïðàâî÷íàÿ ñèñòåìà HTML Help


В настоящее время справочная информация большинства прикладных про-
грамм отображается в формате HTML Help (рис. 8.1).

Рис. 8.1. Пример отображения справочной информации в формате HTML Help


Ãëàâà 8. Ñïðàâî÷íàÿ èíôîðìàöèÿ 329

Основой справочной системы этого формата являются компилированные


HTML-документы. Компилированный HTML-документ (chm-файл) пред-
ставляет собой файл, полученный путем объединения (компиляции) отдель-
ных HTML-документов и всех их элементов (в том числе и иллюстраций)
в единый файл.
Отображение справочной информации формата HTML Help обеспечивает
системная утилита hh.exe.
Создать справочную систему в формате HTML Help можно с помощью
Microsoft HTML Help Workshop. Ее можно бесплатно скачать с сайта
Microsoft (http://www.microsoft.com/downloads).

Ïîäãîòîâêà ñïðàâî÷íîé èíôîðìàöèè


Перед тем как приступить к непосредственной работе в Microsoft HTML Help
Workshop, надо подготовить справочную информацию.
В простейшем случае всю справочную информацию можно поместить в один
HTML-файл. Однако если для навигации по справочной системе предполага-
ется использовать вкладку Содержание, в которой будут перечислены разде-
лы справочной информации, то информацию каждого раздела следует помес-
тить в отдельный HTML-файл.
Процесс подготовки справочной информации рассмотрим на примере, созда-
дим с помощью HTML-редактора RAD Studio справочную информацию для
программы Доход по вкладу.
Справочная информация программы будет состоять из пяти разделов: Доход
по вкладу, Сумма вклада, Срок вклада, Процентная ставка и О про-
грамме. Следовательно, необходимо создать пять HTML-документов.
Чтобы начать работу над новым HTML-документом, надо в меню File вы-
брать команду NewOther, в появившемся окне New Items раскрыть вклад-
ку Web Documents и выбрать HTML Page. В результате станет доступна
вкладка Design окна HTML-редактора (рис. 8.2). В верхней части вкладки
Design отображается HTML-страница так, как она будет выглядеть в окне
браузера (в окне программы отображения справочной информации). В ниж-
ней части вкладки Design отображается текущий тег. Таким образом, у разра-
ботчика есть возможность не только редактирования HTML-документа, но и
прямого редактирования тегов (выбрав вкладку Code, можно увидеть весь
HTML-текст).
Процесс создания HTML-документа в режиме Design практически ничем не
отличается от процесса работы в обычном редакторе текста.
330 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 8.2. HTML-редактор (режим Design)

В верхней части окна HTML-редактора (в режиме Design) находится панель


инструментов, используя которую можно задать стиль оформления абзаца
(в том числе указать, что абзац является заголовком), выбрать шрифт, задать
размер и цвет символов. Также на панели находятся командные кнопки, по-
средством которых можно изменить оформление текста (полужирный, кур-
сив), указать, что абзац является элементом списка.
На страницу можно поместить картинку (рекомендуется заранее поместить
файлы картинок в каталог проекта справочной системы). Чтобы это сделать,
нужно установить курсор в ту точку HTML-документа, куда необходимо
вставить картинку, сделать щелчок на кнопке Inser Image и в поле Image
Sourse появившегося окна ввести имя файла иллюстрации.
После того как HTML-документ раздела справочной информации будет го-
тов, его надо сохранить в каталоге проекта справочной системы — выбрать в
меню File команду Save и задать имя файла. В имени рекомендуется указать
порядковый номер раздела. Например, текст раздела Доход по вкладу следу-
ет сохранить в файле profit_01.htm, раздела Сумма вклада — в файле
profit_02.htm и т. д.
Ãëàâà 8. Ñïðàâî÷íàÿ èíôîðìàöèÿ 331

Ñîçäàíèå chm-ôàéëà
После того как справочная информация в виде отдельных HTML-документов
будет подготовлена, можно приступить к работе в Microsoft HTML Help
Workshop.

Ôàéë ïðîåêòà
Работа над новым chm-файлом начинается с создания файла проекта. Сначала
в меню File надо выбрать команду NewProject, затем в окне New Project
задать имя файла проекта (файл проекта надо создать в том каталоге, в кото-
ром находятся HTML-файлы справочной информации). Следует обратить
внимание, что по умолчанию имя chm-файла, который будет создан в процес-
се компиляции, совпадает с именем файла проекта.
В качестве примера на рис. 8.3 приведено окно HTML Help Workshop в на-
чале работы над проектом справочной системы программы Доход по вкладу.

Рис. 8.3. Окно HTML Help Workshop в начале работы над новым проектом

После того как файл проекта будет создан, надо сформировать список фай-
лов, в которых находится справочная информация (предполагается, что все
необходимые HTML-файлы находятся в одном каталоге — в том же, где со-
хранен файл проекта).
332 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

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


1. Сделать щелчок на кнопке Add topic files.
2. В появившемся окне Topic Files сделать щелчок на кнопке Add.
3. В окне Открыть выбрать HTML-файлы, в которых находится справочная
информация (нажать клавишу <Ctrl> и, удерживая ее нажатой, щелкнуть
на именах нужных файлов).
В результате описанных действий в окне Topic Files (рис. 8.4) появится спи-
сок файлов, содержащих справочную информацию. После щелчка на кнопке
OK в файл проекта (его содержимое отображается на вкладке Project) будет
добавлен раздел FILES, в котором будут перечислены HTML-файлы, содер-
жащие справочную информацию (рис. 8.5).

Рис. 8.4. Формирование списка файлов,


в которых находится справочная информация

Следующее, что нужно сделать, — это задать главную страницу и заголовок


окна справочной информации. Заголовок окна и имя файла главной страницы
надо ввести, соответственно, в поля Title и Default file вкладки General окна
Options (рис. 8.6), которое становится доступным в результате щелчка на
кнопке Change project options (см. pис. 8.3).

Îãëàâëåíèå
Если для навигации по справочной системе предполагается использовать
вкладку Оглавление, то надо создать файл контента. Чтобы это сделать,
Ãëàâà 8. Ñïðàâî÷íàÿ èíôîðìàöèÿ 333

Рис. 8.5. В разделе FILES перечислены файлы,


в которых находится справочная информация

Рис. 8.6. В поле Title надо ввести заголовок окна справочной информации,
а в поле Default file — имя файла главной страницы
334 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

Рис. 8.7. Вкладка Contents

нужно щелкнуть на ярлычке Contents, подтвердить создание нового файла


контента (Create a new contents file) и задать его имя (в качестве имени фай-
ла контента рекомендуется использовать имя проекта). В результате в катало-
ге проекта создания справочной системы появится файл контента (hhc-файл)
и станет доступной вкладка Contents (рис. 8.7).
Оглавление справочной информации формируется путем добавления элемен-
тов на вкладку Contents.
Чтобы на вкладку Contents добавить элемент, надо:
1. Щелкнуть на кнопке Insert a page.
2. В поле Entry title вкладки General окна Table of Contents Entry ввести
название раздела справочной информации и щелкнуть на кнопке Add
(рис. 8.8). В результате станет доступным окно File or URL (рис. 8.9),
в списке HTML titles которого будут перечислены названия HTML-
документов, включенных в проект (если в HTML-файле нет тега <Title>,
то вместо названия документа отображается имя файла).
3. В списке HTML titles окна Path or URL выбрать HTML-документ, кото-
рый надо отобразить в окне справочной информации в результате выбора
ссылки на страницу (заголовок), щелкнуть на кнопке OK (вновь станет
доступным окно Table of Contents Entry).
В результате указанных действий на вкладке Contents появится строка с на-
званием раздела справочной информации.
Ãëàâà 8. Ñïðàâî÷íàÿ èíôîðìàöèÿ 335

Рис. 8.8. В поле Entry title надо ввести название раздела


и щелкнуть на кнопке Add

Рис. 8.9. В списке HTML titles надо выбрать HTML-страницу


336 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

В качестве примера на рис. 8.10 приведено окно HTML Help Workshop,


вкладка Contents которого содержит оглавление справочной информации
программы Доход по вкладу.

Рис. 8.10. Вкладка Contents содержит оглавление справочной информации

Èäåíòèôèêàòîðû ðàçäåëîâ
Обычно когда пользователь, нажав клавишу <F1>, запрашивает справочную
информацию, в окне справочной системы сразу отображается нужный раздел.
Такой режим отображения справочной информации называется контекстно-
зависимым. Для того чтобы программист мог реализовать контекстно-
зависимый режим отображения справочной информации, необходимо каждой
странице назначить идентификатор.
Инструкция назначения идентификатора в общем виде выглядит так:
#define Страница Идентификатор; комментарий

Например, инструкция
#define profit_01 1; страница Доход по вкладу

объявляет идентификатор для страницы profit_01.htm.


Следует обратить внимание, что при объявлении идентификатора расшире-
ние файла не указывается.
Ãëàâà 8. Ñïðàâî÷íàÿ èíôîðìàöèÿ 337

Инструкции объявления идентификаторов следует поместить в отдельный


файл. В листинге 8.1 показан файл объявления идентификаторов разделов
справочной информации программы Доход по вкладу. Следует обратить
внимание, что h-файл — это обычный текстовый файл.

Листинг 8.1. Идентификаторы разделов справочной информации (profit.h)

#define profit_01 1; Доход по вкладу


#define profit_02 2; Сумма вклада
#define profit_03 3; Процентная ставка
#define profit_04 4; Срок вклада
#define profit_05 5; О программе

После того как h-файл будет подготовлен (его следует сохранить в каталоге
проекта справочной системы), надо:
1. Выбрать вкладку Project и сделать щелчок на кнопке HtmlHelp API
Information.
2. Выбрать вкладку Map и щелкнуть кнопку Header file.

Рис. 8.11. Раздел MAP содержит ссылку на файл идентификаторов


разделов справочной информации
338 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

3. В появившемся окне Include file ввести имя файла, в котором находятся


объявления идентификаторов разделов справочной информации, и щелк-
нуть кнопку OK.
В результате описанных действий в файл проекта будет добавлен раздел
MAP (рис. 8.11), в котором будет ссылка (директива #include) на файл объ-
явления идентификаторов разделов справочной информации.

Êîìïèëÿöèÿ
После того как будут заданы файлы, в которых находится справочная инфор-
мация, и созданы файлы содержания (контента) и идентификаторов разделов
справочной информации, можно выполнить компиляцию — объединить от-
дельные страницы (файлы) справочной информации в файл справочной ин-
формации формата HTML Help (chm-файл).
Чтобы выполнить компиляцию, надо в меню File выбрать команду Compile и
в появившемся окне Create a compiled file (рис. 8.12) сделать щелчок на
кнопке Compile. Если перед тем как активизировать процесс компиляции ус-
тановить флажок Automatically display compiled help file when done (пока-
зать откомпилированный файл справки), то по окончании процесса компиля-
ции на экране появится окно справочной информации.

Рис. 8.12. Чтобы создать chm-файл, надо щелкнуть на кнопке Compile

Îòîáðàæåíèå ñïðàâî÷íîé èíôîðìàöèè


Отображение справочной информации в формате HTML Help обеспечивает
утилита hh.exe, которая по отношению к приложению является внешней про-
граммой.
Запуск внешних программ обеспечивает функция WinExec. В качестве пара-
метров функции WinExec надо указать команду запуска программы и режим
Ãëàâà 8. Ñïðàâî÷íàÿ èíôîðìàöèÿ 339

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


записывается так, как если бы она набиралась в окне Запуск программы
операционной системы. Например, инструкция запуска утилиты hh.exe с
целью отображения справочной информации, которая находится в файле
profit.chm, выглядит так:
WinExec('hh.exe profit.chm', SW_RESTORE);

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


справочной информации, в котором будет отображено содержимое главного
раздела.
Если необходимо отобразить конкретный раздел справочной информации, то
в команде запуска утилиты hh.exe надо указать параметр -mapid и идентифи-
катор нужного раздела. Например, в результате выполнения инструкции
WinExec('hh.exe –mapid 3 profit.chm', SW_RESTORE);

на экране появится окно справочной информации, в котором будет отобра-


жена информация раздела, идентификатор которого равен 3.
Следующая программа (ее форма приведена на рис. 8.13) демонстрирует ото-
бражение справочной информации. Окно справочной информации открыва-
ется в результате щелчка на кнопке Справка.
Функция обработки события Click на кнопке Справка приведена в листин-
ге 8.2.

Рис. 8.13. Справочная информация отображается в результате щелчка на кнопке Справка

Листинг 8.2. Отображение справочной информации

// Щелчок на кнопке Справка


void __fastcall TForm1::Button2Click(TObject *Sender)
340 ×àñòü II. Ïðàêòèêóì ïðîãðàììèðîâàíèÿ

HWND h; // идентификатор (дескриптор) окна

h = FindWindow("HH Parent","Доход по вкладу");


if ( h == 0 )
WinExec("hh.exe profit.chm",SW_RESTORE);
else
{
ShowWindow(h,SW_RESTORE);
SetForegroundWindow(h);
}
}

Необходимо обратить внимание на следующее. Утилита hh.exe является


внешней программой. Поэтому если не предпринимать никаких усилий, и в
функции обработки события Click на кнопке Справка указать только инст-
рукцию запуска утилиты hh.exe, то каждый щелчок на кнопке Справка будет
запускать новый экземпляр утилиты hh.exe (открывать новое окно справоч-
ной информации). Чтобы избежать ситуации, когда одновременно будут от-
крыты несколько окон, в которых отображается одна и та же справочная ин-
формация, в процедуру обработки события Click добавлен код, обеспечи-
вающий запуск утилиты hh.exe только в том случае, если окно справочной
информации программы Доход по вкладу не открыто. Функция FindWindow
(найти окно) проверяет, открыто окно справочной информации или нет.
В качестве параметров функции FindWindow передается тип окна (в результа-
те запуска утилиты hh.exe создается окно класса HH Parent) и текст, который
должен быть в заголовке искомого окна (в заголовке окна справочной ин-
формации программы Доход по вкладу отображается текст "Доход по вкла-
ду"). Если окно справочной информации не найдено (в этом случае значение
функции FindWindow равно нулю), то функция WinExec запускает утилиту
hh.exe. Если программа отображения справочной информации уже запущена
(окно программы существует), то функция ShowWindow делает окно актив-
ным, а функция SetForegrounWindow перемещает его на передний план.
Функция обработки события Close формы (листинг 8.3) использует функцию
FindWindow, чтобы проверить, открыто ли окно справочной информации.
Если окно открыто, то функция SendMessage направляет окну, в котором
отображается справочная информация, сообщение (команду) WM_CLOSE (за-
крыть). В результате окно справочной информации закрывается.
Ãëàâà 8. Ñïðàâî÷íàÿ èíôîðìàöèÿ 341

Листинг 8.3. Обработка события Close

// завершение работы программы


void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
HWND h;

h = FindWindow("HH Parent","Доход по вкладу");


if ( h != 0)
// открыто окно справочной информации
SendMessage(h,WM_CLOSE,0,0);
}
ÃËÀÂÀ 9

Ñîçäàíèå
óñòàíîâî÷íîãî äèñêà
Перенести созданную в C++ Builder программу на другой компьютер можно,
например, просто скопировав файлы программы на диск компьютера пользо-
вателя. При этом следует понимать, что программа, которая работает на ком-
пьютере разработчика, у пользователя может и не запуститься (например, из-
за отсутствия нужных ей библиотек, драйверов или файлов данных).
Задачу установки и настройк